View/ViewGroup 的测量(measure )流程
上文可知,ViewRootImpl.performMeasure中最终是跳到了ViewGroup的Measure方法中来了,我们来看看ViewGroup.Measure()方法。
ViewGroup的Measure
ViewGroup没有measure方法。

稍安勿躁,viewGroup继承自view,ViewGroup没有,就看View的。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        boolean optical = isLayoutModeOptical(this);
        if (optical != isLayoutModeOptical(mParent)) {
        ...
        if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
                widthMeasureSpec != mOldWidthMeasureSpec ||
                heightMeasureSpec != mOldHeightMeasureSpec) {
            ...
            if (cacheIndex < 0 || sIgnoreMeasureCache) {
                // measure ourselves, this should set the measured dimension flag back
                onMeasure(widthMeasureSpec, heightMeasureSpec);
                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            } 
        ...
}
这里调用到了view的onMeasure()方法。
- 如果自己是ViewGroup,而ViewGroup又没有默认的onMeasure方法,所以一般继承自ViewGroup的类都会自己重写onMeasure方法。里面必定会遍历自己的子view,并执行child.measure()方法。 
- 如果自己是view的话,测量自己,view.onMeasure() 
ViewGroup的measureChildren
measureChildren是viewGroup提供的测量子view的方法,继承自他的子类有的会用他,也有的会自己写,所以各不相同。这里拿ViewGroup.measureChildren来说。
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
    final int size = mChildrenCount;
    final View[] children = mChildren;
    for (int i = 0; i < size; ++i) {
        final View child = children[i];
        if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
        }
    }
}
只要状态不是GONE,全用measureChild测量出来;
protected void measureChild(View child, int parentWidthMeasureSpec,
        int parentHeightMeasureSpec) {
    final LayoutParams lp = child.getLayoutParams();
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom, lp.height);
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
也很清晰,先用获取到子view的LayoutParams属性,再用getChildMeasureSpec()方法获取到childMeasureSpec,最后调用child的measure方法。
关键代码是child的measureSpec是怎么确认的,就在 getChildMeasureSpec()方法里。传入了三个参数:
- parent的measureSpec
- padding值
- view的LayoutParam中设置的width(或height)
(代码看到了switch case,里面还包了几个if,就知道大概什么情况了,具体解释后面有图)
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);
    int size = Math.max(0, specSize - padding);
    int resultSize = 0;
    int resultMode = 0;
    switch (specMode) {
    // Parent has imposed an exact size on us
    case MeasureSpec.EXACTLY:
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size. So be it.
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    // Parent has imposed a maximum size on us
    case MeasureSpec.AT_MOST:
        if (childDimension >= 0) {
            // Child wants a specific size... so be it
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size, but our size is not fixed.
            // Constrain child to not be bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    // Parent asked to see how big we want to be
    case MeasureSpec.UNSPECIFIED:
        if (childDimension >= 0) {
            // Child wants a specific size... let him have it
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size... find out how big it should
            // be
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size.... find out how
            // big it should be
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
        break;
    }
    //noinspection ResourceType
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
根据 parent的measureSpec 和 自己的layoutparam 的(33)9种情况组合,可以确定下自己的MeasureSpec*。具体如图:

如此一来,从顶层的ViewGroup,到最后的View,每个View的MeasureSpec都将被确定下来。
然后children.measure又调用了measure()方法。
view的onMeasure()如下:
首先明白一点,(默认这里的view不是viewGroup)到这里为止的话,MeasureSpec已经确定下来了,下面是根据spec的值确认宽高到底长度是多少。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(
        getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
        getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)
    );
}
按照方法执行的顺序,由内到外看吧。
First,getSuggestedMinimumWidth方法:
    /**
     * Returns the suggested minimum width that the view should use. This
     * returns the maximum of the view's minimum width
     * and the background's minimum width
     *  ({@link android.graphics.drawable.Drawable#getMinimumWidth()}).
     * <p>
     * When being used in {@link #onMeasure(int, int)}, the caller should still
     * ensure the returned width is within the requirements of the parent.
     *
     * @return The suggested minimum width of the view.
     */
//返回建议的最小值。即【view的最小值 和 background的最小值】中的最大值。
//当在onMeasure中调用的时候,调用者仍应保证返回值在父级的范围之内。
protected int getSuggestedMinimumWidth() {
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
解释我放在了注释中。一句话:返回的是系统建议的值。【getSuggestedMinimumHeight同理,一个宽,一个高】
再看getDefaultSize(),如下:
public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);
    switch (specMode) {
    case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}
通过specMode的值,判断到底是用系统建议的值,还是用measureSpec中的值,并将这个值返回。
最后,最外层setMeasuredDimension()调用这个值 ,作用是用来设置View的宽高的。
/**
此方法必须由onMeasure(int, int)被调用,以存储所测量的宽度和测量高度。
如果不这样做会引发在测量时间的异常。
**/
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
    boolean optical = isLayoutModeOptical(this);
    if (optical != isLayoutModeOptical(mParent)) {
        Insets insets = getOpticalInsets();
        int opticalWidth  = insets.left + insets.right;
        int opticalHeight = insets.top  + insets.bottom;
        measuredWidth  += optical ? opticalWidth  : -opticalWidth;
        measuredHeight += optical ? opticalHeight : -opticalHeight;
    }
    setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
所以一般自己自定义view的时候,沿用这个方法就行了,这个方法传入进去的两个值就是最后的测量结果了。
通用自定义view
最后,附上一个通用自定义view的onMeasure部分的代码实现:
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));
    }
    protected int measure(int measureSpec, boolean WOH) {
        int size = MeasureSpec.getSize(measureSpec);
        int mode = MeasureSpec.getMode(measureSpec);
        int measured;
        if (mode == MeasureSpec.EXACTLY) {
            measured = size;
        } else {
            int measureMinimum = WOH ? getMinimumMeasureWidth() : getMinimumMeasureHeight();
          // 根据内容计算最小值
          // measureMinimum = Math.max(measureMinimum, MIN_CONTENT_SIZE);
            if (WOH) {
                measureMinimum = Math.max(measureMinimum, measureMinimum + getPaddingLeft() + getPaddingRight());
            } else {
                measureMinimum = Math.max(measureMinimum, measureMinimum + getPaddingTop() + getPaddingBottom());
            }
            measured = measureMinimum;
            if (mode == MeasureSpec.AT_MOST) {
                measured = Math.min(measured, size);
            }
        }
        return measured;
    }
测量完成后,下一步,layout。










