首页 > 代码库 > Android measure过程分析

Android measure过程分析

  作为一名Android开发人员,我们都知道一个View从无到有,会经历3个阶段:

1. measure/测量阶段,也就是确定某个view大小的过程;

2. layout/布局阶段,也就是确定其左上右下坐标的过程;

3. draw/绘制阶段,也就是按照前面2步计算的结果,将view绘制在屏幕相应的位置上;

今天,我带领大家来看看View系统的measure过程。到现在相信大部分人都知道measure是从ViewRootImpl.measureHierarchy

方法开始的,但归根结底是从performTraversals开始的。

  为了从一开始就清楚onMeasure(int widthMeasureSpec, int heightMeasureSpec)的这2个参数从哪来的,虽然我们都知道

这2个参数表示parent施加给我们的约束,但可能大部分人不明白程序run起来的时候这些值都是从哪里来的。为了弄清楚这个问题,

我们还得从上面ViewRootImpl的measureHierarchy说起,来看其源码:

    private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,            final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {        int childWidthMeasureSpec;        int childHeightMeasureSpec;        boolean windowSizeMayChange = false;        if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG,                "Measuring " + host + " in display " + desiredWindowWidth                + "x" + desiredWindowHeight + "...");        boolean goodMeasure = false;        if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) { // 在WRAP_CONTENT的情况下,先从一个prefered值开始measure            // On large screens, we don‘t want to allow dialogs to just            // stretch to fill the entire width of the screen to display            // one line of text.  First try doing the layout at a smaller            // size to see if it will fit.            final DisplayMetrics packageMetrics = res.getDisplayMetrics();            res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);            int baseSize = 0;            if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {                baseSize = (int)mTmpValue.getDimension(packageMetrics);            }            if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);            if (baseSize != 0 && desiredWindowWidth > baseSize) { // 如果baseSize真小的话,用baseSize先measure一遍试试                childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);                childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);                if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("                        + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");                if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {                    goodMeasure = true; // measure的结果合适                } else {                    // Didn‘t fit in that size... try expanding a bit.                    baseSize = (baseSize+desiredWindowWidth)/2; // 加大baseSize重新执行上述过程                    if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="                            + baseSize);                    childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);                    if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("                            + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");                    if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {                        if (DEBUG_DIALOG) Log.v(TAG, "Good!");                        goodMeasure = true;                    }                }            }        }        if (!goodMeasure) { // 如果用baseSize measure的结果不合适,则老老实实用提供的参数重新measure一遍            childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);            if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {                windowSizeMayChange = true;            }        }        if (DBG) {            System.out.println("======================================");            System.out.println("performTraversals -- after measure");            host.debug();        }        return windowSizeMayChange;    }    /**     * Figures out the measure spec for the root view in a window based on it‘s     * layout params.     *     * @param windowSize     *            The available width or height of the window     *     * @param rootDimension     *            The layout params for one dimension (width or height) of the     *            window.     *     * @return The measure spec to use to measure the root view.     */    private static int getRootMeasureSpec(int windowSize, int rootDimension) { // 通过具体的windowSize和提供的spec        int measureSpec;                                                       // 构造一个合适的Root MeasureSpec        switch (rootDimension) {  // 这里的windowSize就是设备的宽、高,rootDimension就是xml文件里指定的layoutparam        case ViewGroup.LayoutParams.MATCH_PARENT:            // Window can‘t resize. Force root view to be windowSize. 设置root view就是window这么大            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);            break;        case ViewGroup.LayoutParams.WRAP_CONTENT:            // Window can resize. Set max size for root view. 设置root view最多是window这么大            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);            break;        default:            // Window wants to be an exact size. Force root view to be that size. 某一个具体的大小            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);            break;        }        return measureSpec;    }

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try {
// 调用root view的measure方法,从此进入到view层次结构,顺便也把MeasureSpec带了进去。。。 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }

  从之前的文章中,我们知道DecorView实际上是继承至FrameLayout,由于它和ViewGroup都没有重载这个方法,实际上也没法重载,

因为这个方法是View的一个final方法,代码如下:

/**     * <p>     * This is called to find out how big a view should be. The parent     * supplies constraint information in the width and height parameters.     * </p>     *     * <p>     * The actual measurement work of a view is performed in     * {@link #onMeasure(int, int)}, called by this method. Therefore, only     * {@link #onMeasure(int, int)} can and must be overridden by subclasses.     * </p>     *     *     * @param widthMeasureSpec Horizontal space requirements as imposed by the     *        parent     * @param heightMeasureSpec Vertical space requirements as imposed by the     *        parent     *     * @see #onMeasure(int, int)     */    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {        boolean optical = isLayoutModeOptical(this);        if (optical != isLayoutModeOptical(mParent)) {            Insets insets = getOpticalInsets();            int oWidth  = insets.left + insets.right;            int oHeight = insets.top  + insets.bottom;            widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);            heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);        }        // Suppress sign extension for the low bytes        long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;        if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);        if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||                widthMeasureSpec != mOldWidthMeasureSpec ||                heightMeasureSpec != mOldHeightMeasureSpec) {            // first clears the measured dimension flag            mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;            resolveRtlPropertiesIfNeeded();            int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :                    mMeasureCache.indexOfKey(key);            if (cacheIndex < 0 || sIgnoreMeasureCache) {                // measure ourselves, this should set the measured dimension flag back                onMeasure(widthMeasureSpec, heightMeasureSpec); // 注意这个调用,这个方法是本文的重点                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;            } else {                long value =http://www.mamicode.com/ mMeasureCache.valueAt(cacheIndex);                // Casting a long to int drops the high 32 bits, no mask needed                setMeasuredDimension((int) (value >> 32), (int) value);                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;            }            // flag not set, setMeasuredDimension() was not invoked, we raise            // an exception to warn the developer            if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {                throw new IllegalStateException("onMeasure() did not set the"                        + " measured dimension by calling"                        + " setMeasuredDimension()");            }            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;        }        mOldWidthMeasureSpec = widthMeasureSpec;        mOldHeightMeasureSpec = heightMeasureSpec;        mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |                (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension    }

正如方法doc说的那样,真正的measure过程是发生在onMeasure方法中的,所以你可以也应该override这个方法,我们紧接着看看View中

的默认实现,代码如下:

    /**     * <p>     * Measure the view and its content to determine the measured width and the     * measured height. This method is invoked by {@link #measure(int, int)} and     * should be overriden by subclasses to provide accurate and efficient     * measurement of their contents.     * </p>     *     * <p>     * <strong>CONTRACT:</strong> When overriding this method, you     * <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the     * measured width and height of this view. Failure to do so will trigger an     * <code>IllegalStateException</code>, thrown by     * {@link #measure(int, int)}. Calling the superclass‘     * {@link #onMeasure(int, int)} is a valid use.     * </p>     *     * <p>     * The base class implementation of measure defaults to the background size,     * unless a larger size is allowed by the MeasureSpec. Subclasses should     * override {@link #onMeasure(int, int)} to provide better measurements of     * their content.     * </p>     *     * <p>     * If this method is overridden, it is the subclass‘s responsibility to make     * sure the measured height and width are at least the view‘s minimum height     * and width ({@link #getSuggestedMinimumHeight()} and     * {@link #getSuggestedMinimumWidth()}).     * </p>     *     * @param widthMeasureSpec horizontal space requirements as imposed by the parent.     *                         The requirements are encoded with     *                         {@link android.view.View.MeasureSpec}.     * @param heightMeasureSpec vertical space requirements as imposed by the parent.     *                         The requirements are encoded with     *                         {@link android.view.View.MeasureSpec}.     *     * @see #getMeasuredWidth()     * @see #getMeasuredHeight()     * @see #setMeasuredDimension(int, int)     * @see #getSuggestedMinimumHeight()     * @see #getSuggestedMinimumWidth()     * @see android.view.View.MeasureSpec#getMode(int)     * @see android.view.View.MeasureSpec#getSize(int)     */    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));    }    /**     * <p>This method must be called by {@link #onMeasure(int, int)} to store the     * measured width and measured height. Failing to do so will trigger an     * exception at measurement time.</p>     *     * @param measuredWidth The measured width of this view.  May be a complex     * bit mask as defined by {@link #MEASURED_SIZE_MASK} and     * {@link #MEASURED_STATE_TOO_SMALL}.     * @param measuredHeight The measured height of this view.  May be a complex     * bit mask as defined by {@link #MEASURED_SIZE_MASK} and     * {@link #MEASURED_STATE_TOO_SMALL}.     */    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;        }        mMeasuredWidth = measuredWidth; // 赋值        mMeasuredHeight = measuredHeight;        mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET; // 设置标志位    }    /**     * Returns the suggested minimum height that the view should use. This     * returns the maximum of the view‘s minimum height     * and the background‘s minimum height     * ({@link android.graphics.drawable.Drawable#getMinimumHeight()}).     * <p>     * When being used in {@link #onMeasure(int, int)}, the caller should still     * ensure the returned height is within the requirements of the parent.     *     * @return The suggested minimum height of the view.     */    protected int getSuggestedMinimumHeight() {// 找到view的最小大小,没background的时候返回mMinHeight,这个你可以在xml中指定        return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());                                               // 否则返回mMinHeight和background的最小值里的较大者    }    /**     * 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.     */    protected int getSuggestedMinimumWidth() {        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());    }    /**     * Utility to return a default size. Uses the supplied size if the     * MeasureSpec imposed no constraints. Will get larger if allowed     * by the MeasureSpec.     *     * @param size Default size for this view     * @param measureSpec Constraints imposed by the parent     * @return The size this view should be.     */    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: // parent imposed的spec没指定,则用自己的值default size            result = size;            break;        case MeasureSpec.AT_MOST:        case MeasureSpec.EXACTLY: // 否则不论是精确指定或是至多,都用spec提供的值            result = specSize;            break;        }        return result;    }

我们看到View.onMeasure方法只是提供了一个通用的、一般的实现,子类一般需要重载它,自己提供更加合理、高效的实现,最重要的是

符合你的需求。同时我们也看到ViewGroup并没有提供它自己的实现,但是提供了一些在measure过程中很有用的方法,其特定子类如

FrameLayout、LinearLayout等在measure过程中都需要用到的。

  为了更进一步看看这个过程,我们这里以FrameLayout的onMeasure为例分析下,看其源码:

    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int count = getChildCount();        // matchparent child标记        final boolean measureMatchParentChildren =                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||                MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;        mMatchParentChildren.clear(); // 清空列表        int maxHeight = 0;        int maxWidth = 0;        int childState = 0;        // 遍历children,初步找出maxHeight和maxWidth,顺便构造mMatchParentChildren列表        for (int i = 0; i < count; i++) {            final View child = getChildAt(i);
// 默认情况下,只measure非GONE的child,但你可以设置mMeasureAllChildren来打破这一限制
if (mMeasureAllChildren || child.getVisibility() != GONE) {
// 调用parent,ViewGroup提供的方法。。。 measureChildWithMargins(child, widthMeasureSpec,
0, heightMeasureSpec, 0);
// 我们平时在xml文件中设置的android:layout_xxx其实就是这里的LayoutParams
final LayoutParams lp = (LayoutParams) child.getLayoutParams(); maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); // 添加matchparent child } } } } // 进一步调整maxWidth、maxHeight的值,考虑foreground padding、minimum height/width,还有foreground的最小值 // Account for padding too maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); // Check against our minimum height and width maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground‘s minimum height and width final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } // 设置FrameLayout自身的measuredWidth、measuredHeight setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); count = mMatchParentChildren.size(); if (count > 1) { // 如果有matchparent child的话 for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); int childWidthMeasureSpec; int childHeightMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) {
// 如果是MATCH_PARENT的话,由于parent已经measure过了,所以就相当于child指定了确定值,
// 所以用的MeasureSpec.EXACTLY。。。 childWidthMeasureSpec
= MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin,// 在算child大小的时候要去掉parent的padding,child自己指定的各种margin MeasureSpec.EXACTLY); } else {
// 否则,根据parent的measureSpec,已经用掉的大小,child的layoutparam的信息,创建一个合适的MeasureSpec childWidthMeasureSpec
= getChildMeasureSpec(widthMeasureSpec, getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin, lp.width); } if (lp.height == LayoutParams.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTopWithForeground() + getPaddingBottomWithForeground() + lp.topMargin + lp.bottomMargin, lp.height); } // 用新的childWidthMeasureSpec、childHeightMeasureSpec再次measure child child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } }

  接下来,我们重点看看涉及到的几个ViewGroup方法,代码如下:

    /**     * Ask one of the children of this view to measure itself, taking into     * account both the MeasureSpec requirements for this view and its padding     * and margins. The child must have MarginLayoutParams The heavy lifting is     * done in getChildMeasureSpec.     *     * @param child The child to measure     * @param parentWidthMeasureSpec The width requirements for this view     * @param widthUsed Extra space that has been used up by the parent     *        horizontally (possibly by other children of the parent)     * @param parentHeightMeasureSpec The height requirements for this view     * @param heightUsed Extra space that has been used up by the parent     *        vertically (possibly by other children of the parent)     */    protected void measureChildWithMargins(View child,            int parentWidthMeasureSpec, int widthUsed,            int parentHeightMeasureSpec, int heightUsed) {        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin // 考虑上parent的padding和child的margin                        + widthUsed, lp.width);        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin                        + heightUsed, lp.height);        // 这里如果child是个ViewGroup类型,则实际会递归下去。。。        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);    }    /**     * Does the hard part of measureChildren: figuring out the MeasureSpec to     * pass to a particular child. This method figures out the right MeasureSpec     * for one dimension (height or width) of one child view.     *     * The goal is to combine information from our MeasureSpec with the     * LayoutParams of the child to get the best possible results. For example,     * if the this view knows its size (because its MeasureSpec has a mode of     * EXACTLY), and the child has indicated in its LayoutParams that it wants     * to be the same size as the parent, the parent should ask the child to     * layout given an exact size.     *     * @param spec The requirements for this view     * @param padding The padding of this view for the current dimension and     *        margins, if applicable     * @param childDimension How big the child wants to be in the current     *        dimension     * @return a MeasureSpec integer for the child     */     public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
// 这个方法是协商型的,最终结果既可能直接由spec(parent提供的),也可能由childDimension决定
// 所以我们知道了,一个View的大小不是简单的单方面决定的,而是通过一系列条件协商的结果,
// 有时会尊重parent的spec,有时会坚持自己的dimension要求
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: // parent说child你应该是个确定的大小 if (childDimension >= 0) { // child正好设置了确定的大小 resultSize = childDimension; // 让child是那个确定的大小 resultMode = MeasureSpec.EXACTLY; // 设置mode为EXACTLY } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; // 其他情况下都是parent spec中的大小,只是mode不同 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; // 不能超过size resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: // parent说child你应该最大是某个值。。。 if (childDimension >= 0) { // child指定确定值了,则听child的 // 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: // parent没对child的大小有啥要求 if (childDimension >= 0) { // child指定了确定的值,听child的 // 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 = 0; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }

  至此我们已经将measure过程的相关代码大致分析了下,进一步的理解还需要大家在开发中慢慢体会、细细研读源码,最后附上一篇

不错的同主题文章:http://www.cnblogs.com/xilinch/archive/2012/10/24/2737178.html。

Android measure过程分析