Android View的测量流程(Measure)

1、View和ViewGroup

- Android应用中的所有用户界面元素都是使用View和ViewGroup对象构建而成。
- View对象用于在屏幕上绘制可供用户交互的内容。
- ViewGroup对象用于存储其他View(和ViewGroup)对象,以便定义界面的布局。ViewGroup也是继承自View。
- Android提供了一系列View和ViewGroup子类,可提供常用输入空间(如按钮和文本字段)和各种布局模式(如线性布局和相对布局)。

2、用户界面布局

每个应用组件的用户界面都是使用View和ViewGroup对象的层次结构定义的。如图所示:
这里写图片描述
每个ViewGroup都是要给看不见的用于组织View的容器,而它的子View可能是输入控件或者在UI上绘制了某块区域的小部件。有了层次树,就可以根据自己的需要,设计简单或者负责的布局(布局越简单性能越好)

3、View measure过程
3.1 performTraversals方法

DecorView是当前Activity的ViewTree的根节点,View的绘制要从根节点开始,具体是从ViewRootImpl的的performTraversals方法开始的,它经过measure、layout和draw三个过程才能最终将一个View绘制出来,其中measure用来测量View的宽和高,layout用来确定View在父容器中的放置位置,而draw则负责将View绘制在屏幕上。
performTraversals源码:

    private void performTraversals() {
        ...省略...

        //执行performMeasure方法
        if (mFirst || windowShouldResize || insetsChanged ||
                viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
            if (!mStopped || mReportNextDraw) {
                boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                        (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
                if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                        || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
                        updatedConfiguration) {
                    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

                    ...省略...

                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

                    // Implementation of weights from WindowManager.LayoutParams
                    // We just grow the dimensions as needed and re-measure if
                    // needs be
                    int width = host.getMeasuredWidth();
                    int height = host.getMeasuredHeight();
                    boolean measureAgain = false;

                    if (lp.horizontalWeight > 0.0f) {
                        width += (int) ((mWidth - width) * lp.horizontalWeight);
                        childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
                                MeasureSpec.EXACTLY);
                        measureAgain = true;
                    }
                    if (lp.verticalWeight > 0.0f) {
                        height += (int) ((mHeight - height) * lp.verticalWeight);
                        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
                                MeasureSpec.EXACTLY);
                        measureAgain = true;
                    }

                    if (measureAgain) {
                        if (DEBUG_LAYOUT) Log.v(mTag,
                                "And hey let's measure once more: width=" + width
                                + " height=" + height);
                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                    }

                    layoutRequested = true;
                }
            }
        } else {
            ...省略...
        }

        ...省略...

        //执行performLayout方法
        if (didLayout) {
            performLayout(lp, mWidth, mHeight);

            ...省略...

        }

        ...省略...

        //执行performDraw方法
        if (!cancelDraw && !newSurface) {
            ...省略...

            performDraw();
        } else {
            ...省略...
        }
        ...省略...
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75

这个方法的源码有700多行,针对其大致流程,用流程图来表示一下:
这里写图片描述

如图所示,performTraversals会依次调用performMeasure、performLayout和performDraw三个方法分别完成ViewTree中的根节点DecorView的measure、layout和draw这三大流程,其中performMeasure中会调用measure方法,在measure方法中又回调用onMeasure方法,在onMeasure方法中则会对所有的子元素进行measure过程,这个时候measure流程就从父容器传递到了子元素中,这样就完成了一次measure过程。接着子元素会重复父容器的measure过程,如此反复就完成了整个View树的遍历。同理,performLayout和PerformDraw的传递过程和performMeasure类似

三大过程说明:
1、measure过程决定了View的宽和高,Measure完成后,可以通过getMeasuredWidth和getMeasuredHeight方法来获取到View测量后的宽高,在几乎所有的情况下它都等同于View的最终宽高。
2、Layout过程决定了View的四个顶点的坐标和实际的View的宽高,完成后,可以通过getTop、getBottom、getLeft、getRight来拿到View的四个顶点的位置,并可以通过getWidth和getHeight方法来拿到View的最终宽高。
3、Draw过程则决定了View的显示,只有draw方法完成以后View的内容才能呈现在屏幕上。

3.2 MeasureSpec类
MeasureSpec是View的一个静态内部类,源码如下:

    /**
     * A MeasureSpec encapsulates the layout requirements passed from parent to child.
     * Each MeasureSpec represents a requirement for either the width or the height.
     * A MeasureSpec is comprised of a size and a mode. There are three possible
     */
    public static class MeasureSpec {
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

        /**
         * UNSPECIFIED模式
         * 父容器不对子View有任何限制,子View要多大给多大。
         */
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;

        /**
         * EXACTLY模式
         * 父容器已经检测出子View所需要的精确大小,这个时候子View的最终大小
         * 就是SpecSize所指定的值。它对应于LayoutParams中的match_parent和具体的数值这两种模式
         */
        public static final int EXACTLY     = 1 << MODE_SHIFT;

        /**
         * AT_MOST模式
         * 父容器指定了一个可用大小即SpecSize,子View的大小不能大于这个值
         * 对应与LayoutParams中的wrap_content。
         */
        public static final int AT_MOST     = 2 << MODE_SHIFT;

        /**
         * 将size和mode打包称一个32位的int型数值,size和mode由父类的MeasureSpec和子类的LayoutParams决定
         * 高2位表示SpecMode 即测量模式,而SpecSize是值在某种测量模式下的规格大小。
         */
        public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
                                          @MeasureSpecMode int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

        /**
         * 将32位的MeasureSpec解析,返回SpecMode,即测量模式
         */
        public static int getMode(int measureSpec) {
            //noinspection ResourceType
            return (measureSpec & MODE_MASK);
        }

        /**
         * 将32位的MeasureSpec解析,返回SpecSize,即某种测量模式下的规格大小
         */
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }
        ...省略...
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58


MeasureSpec的几点说明:
1、MeasureSpec封装了布局的规格尺寸,即View的宽和高的信息。
2、ViewTree中的每一个节点都持有一个MeasureSpec,封装了自己的尺寸规格。在View的测量流程中,通过makeMeasureSpec来保存宽高信息,在其他流程中,通过getMode或getSize得到模式和宽高。
3、当前View的MeasureSpec是由父容器的MeasureSpec和自身的LayoutParams来共同决定的。
4、但是MeasureSpec并不是值View的测量宽高,是在onMeasure方法中根据MeasureSpec确定View的测量宽高

3.3 Measure流程

3.3.1 ViewGroup的measure过程

以DecorView为例,DecorView继承FrameLayout
先从ViewRootImpl的performTraversals方法开始:

        WindowManager.LayoutParams lp = mWindowAttributes;

        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

    1
    2
    3
    4
    5

想要测量DecorView,就必须有测量的尺寸规格MeasureSpec,通过调用getRootMeasureSpec获取了DecorView的MeasureSpec。

ViewRootImpl的getRootMeasureSpec方法:

    /**
     * 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) {
        int measureSpec;
        switch (rootDimension) {

        case ViewGroup.LayoutParams.MATCH_PARENT:
            // Window can't resize. Force root view to be windowSize.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
            break;
        case ViewGroup.LayoutParams.WRAP_CONTENT:
            // Window can resize. Set max size for root view.
            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;
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

因为DecorView是ViewTree中最顶层的View没有父节点,就没有办法获取父View的MeasureSpec,所以就根据window的尺寸和window的模式来决定DecorView的MeasureSpec。获取之后回到performTraversals方法,
执行performMeasure方法 :

ViewRootImpl的performMeasure方法:

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

    1
    2
    3
    4
    5
    6
    7
    8

这个方法调用了当前View(即DecorView)的measure方法,该方法不能被重写,所以就是调用的View中的measure方法:

View的measure方法:

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
       。。。省略。。。。

        。。。省略。。。。

        if (forceLayout || needsLayout) {
            。。。省略。。。。

            if (cacheIndex < 0 || sIgnoreMeasureCache) {

                onMeasure(widthMeasureSpec, heightMeasureSpec);
                。。。省略。。。。
            } else {
                。。。省略。。。。
            }
            。。。省略。。。。
        }

        。。。省略。。。。
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

可是看到,它在内部调用了onMeasure方法,DecorView对onMeasure进行了重写,所以这里实际上调用的是

DecorView的onMeasure方法:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        。。。省略。。。
        if (widthMode == AT_MOST) {
            。。。省略。。。
        }
        if (heightMode == AT_MOST) {
            。。。省略。。。
        }

        if (mOutsets.top > 0 || mOutsets.bottom > 0) {
            。。。省略。。。
        }
        if (mOutsets.left > 0 || mOutsets.right > 0) {
            。。。省略。。。
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if (!fixedWidth && widthMode == AT_MOST) {
            。。。省略。。。
        }

        // TODO: Support height?

        if (measure) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30

在方法内部,主要是进行了一些判断,最后执行super.onMeasure方法,调用父类的onMeasure方法,即FrameLayout的onMeasure方法。不同的ViewGroup有个不同的性质,那么它们的onMeasure必然是不同的,

以FrameLayout为例:

FrameLayout的onMeasure方法:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //获取子节点的个数
        int count = getChildCount();

        //EXACTLY对应于LayoutParams中的match_parent和具体的数值这两种模式。
        //判断当前布局FrameLayout的宽高是否是match_parent模式或者指定一个精确的大小
        //如果是则置measureMatchParentChildren为false

        final boolean measureMatchParentChildren =
                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
        //mMatchParentChildren是ArrayList集合
        mMatchParentChildren.clear();

        int maxHeight = 0;
        int maxWidth = 0;
        int childState = 0;

        //循环遍历所有类型不为GONE的子View
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (mMeasureAllChildren || child.getVisibility() != GONE) {
                //对每一个子View进行测量
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                获取子View的LayoutParams
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                //寻找子View中宽高最大者,因为如果FrameLayout是wrap_content属性
                //那么它的大小取决于子View的最大者
                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());
                //如果FrameLayout是wrap_content模式,那么往mMatchParentChildren中添加宽或高为match_parent的子View,
                //因为该子View的最终测量大小受到FrameLayout的最终测量大小影响
                if (measureMatchParentChildren) {
                    if (lp.width == LayoutParams.MATCH_PARENT ||
                            lp.height == LayoutParams.MATCH_PARENT) {
                        mMatchParentChildren.add(child);
                    }
                }
            }
        }

        // 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测量结果
        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                resolveSizeAndState(maxHeight, heightMeasureSpec,
                        childState << MEASURED_HEIGHT_STATE_SHIFT));

        //子View中设置为match_parent的个数
        count = mMatchParentChildren.size();
        //只有FrameLayout的模式为wrap_content的时候才会执行下列语句
        //对子View中中模式match_parent的进行重新测量
        if (count > 1) {
            for (int i = 0; i < count; i++) {
                final View child = mMatchParentChildren.get(i);
                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

                //对FrameLayout的宽度规格设置,因为这会影响子View的测量
                final int childWidthMeasureSpec;
                if (lp.width == LayoutParams.MATCH_PARENT) {
                    final int width = Math.max(0, getMeasuredWidth()
                            - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
                            - lp.leftMargin - lp.rightMargin);
                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                            width, MeasureSpec.EXACTLY);
                } else {
                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                            getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
                            lp.leftMargin + lp.rightMargin,
                            lp.width);
                }

                final int childHeightMeasureSpec;
                if (lp.height == LayoutParams.MATCH_PARENT) {
                    final int height = Math.max(0, getMeasuredHeight()
                            - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
                            - lp.topMargin - lp.bottomMargin);
                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                            height, MeasureSpec.EXACTLY);
                } else {
                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                            getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
                            lp.topMargin + lp.bottomMargin,
                            lp.height);
                }

                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106

总结一下FrameLayout的onMeasure过程:首先FrameLayout根据它的MeasureSpec来对每一个子View进行测量,即调用measureChildWithMargin方法,对于每一个测量完成的子View,会寻找其中最大的宽高,那么FrameLayout的测量宽高会受到这个子View的最大宽高的影响(wrap_content模式),接着调用setMeasureDimension方法,把FrameLayout的测量宽高保存。最后,当FrameLayout为wrap_content属性时,如果其子View时match_parent属性的话,则要重新设置FrameLayout的测量规格,然后重新对该部分View测量。

FrameLayout的measureChildWithMargins方法:

    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
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

由源码可知,里面调用了getChildMeasureSpec方法,把父容器的MeasureSpec以及自己的LayoutParams属性传递进去来获取子View的MeasureSpec,这也说明了:子View的MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同决定,最终调用子View的measure方法,重复measure的过程,最终完成了ViewTree的遍历,View的measure过程参考3.3.2小节。

FrameLayout的getChildMeasureSpec方法:

    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);

        //size表示子View可用的空间,是父容器尺寸减去padding
        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:
            。。。省略。。。
            break;

        // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            。。。省略。。。
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41

方法说明:根据不同的父容器的模式及子View的LayoutParams来决定子View的规格尺寸模式等。

小结:至此以DecorView为例,把ViewGroup(FrameLayout)的测量流程梳理了一下:在ViewRootImpl的performTraversals方法中获得DecorView的尺寸,然后在performMeasure方法中开始测量,对于不同的layout布局有着不同的实现方式,但是大体上是在onMeasure方法中,对每一个子View进行遍历,根据ViewGroup的MeasureSpec和子View的LayoutParams来确定自身的测量宽高,然后根据所有子View的测量宽高信息再确定父容器的测量宽高。

3.3.2 View的measure过程

根据上一小节对ViewGroup的measure过程的分析,调用getChildMeasureSpec获取子View的MeasureSpec后,调用子View的measure方法进行测量,并将子View的MeasureSpec作为参数传递给measure方法,measure方法是一个final类型的方法,子类不能重写,在View的measure方法中会去调用onMeasure方法

View的onMeasure方法:

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

    1
    2
    3
    4

这里调用了setMeasuredDimension方法设置测量宽高,而测量宽高则是从getDefaultSize中获取

View的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;
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

根据不同的模式来设置不同的测量宽高,一般只需要看AT_MOST和EXACTLY模式,这两种情况下getDefaultSize返回的就是specSize,而这个specSize就是View测量后的大小。,也就是说这两种模式效果是一样的,根据之前的分析wrap_content对应于AT_MOST,match_content和精确大小对应于EXACTLY,所以对于一个直接继承子View的自定义View来说,它的wrap_content和match_content属性的效果是一样的,因此如果要在布局中使用wrap_content就需要重写onMeasure方法并设置wrap_content是的自身大小。
---------------------
作者:小文是蜀黍
来源:CSDN
原文:https://blog.csdn.net/fighting_sxw/article/details/79501913
版权声明:本文为博主原创文章,转载请附上博文链接!

posted @ 2019-06-15 20:19  天涯海角路  阅读(251)  评论(0)    收藏  举报