XVIII works View, (2) --- understand MeasureSpec

Disclaimer: This article is a blogger original article, shall not be reproduced without the bloggers allowed. https://blog.csdn.net/yz_cfm/article/details/90722051

What MeasureSpec is:

    It is a static inner class in the Android source code View.java:

public static class MeasureSpec {

    private static final int MODE_SHIFT = 30;
    private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

    public static final int UNSPECIFIED = 0 << MODE_SHIFT;
    public static final int EXACTLY     = 1 << MODE_SHIFT;
    public static final int AT_MOST     = 2 << MODE_SHIFT;

    public static int makeMeasureSpec(int size,int mode) {
        if (sUseBrokenMakeMeasureSpec) {
            return size + mode;
        } else {
            return (size & ~MODE_MASK) | (mode & MODE_MASK);
        }
    }

   
    public static int getMode(int measureSpec) {
        return (measureSpec & MODE_MASK);
    }

    public static int getSize(int measureSpec) {
        return (measureSpec & ~MODE_MASK);
    }
}

    It can be seen from the source has an internal static method makeMeasureSpec (), can create a MeasureSpec value by which the return value of type int, so actually representing a 32 MeasureSpec int value . By the getMode () and getSize () method can be seen that it is divided into two parts, namely the upper 2 bits SpecMode, low 30 representatives SpecSize. SpecMode refers to the measurement mode, it refers to the size specifications specsize under certain measurement mode, through the getMode () respectively getSize () method to get. We can see by the above method Android source provided by the View MeasureSpec, we can use the getMode () and getSize () were obtained and it SpecMode specsize; SpecMode by the View and specsize, we can use makeMeasureSpec () method get to it MeasureSpec.

    Note that, MeasureSpec mentioned above refers to a value represented MeasureSpec int, and not MeasureSpec itself, which itself is a static inner class View class.

    SpecMode means above said measurement mode, in the Android system, which defines three cases:

    UNSPECIFIED :( mode is not specified) View parent container does not have any limitation as to how much to, this is generally used internally.

    :( EXACTLY the /  ɪg'zæk (T) lɪ /  fine mode) has been detected accurately parent container View size desired, the final size of the value of this time SpecSize View is designated. It corresponds to LayoutParams match_parent and the specific numerical values of these two modes.

    AT_MOST :( maximum pattern) parent container specifies a final size of available size specsize i.e., View not greater than this value, the specific value depends on what is the realization of different View. It corresponds to the LayoutParams wrap_content.

MeasureSpec role:

    It works during the measurement of View. In the method of measure of View, View, MeasureSpec need passed as parameters. Android system into corresponding MeasureSpec according to the View and MeasureSpec LayoutParams View parent container, and then performing measure () method according to this MeasureSpec value measured last View width / height. So to a large extent it determines the dimensions of a View, the reason that is largely because of child View of MeasureSpec by MeasureSpec the parent container and LayoutParams its own joint decision, not just by the sub-View the LayoutParams to decide.

Relations MeasureSpec and LayoutParams and MeasureSpec conversion process:

    View the time of measurement, the system will View LayoutParams into corresponding MeasureSpec under the constraint of the parent container, and then to determine the width / height after View measured according to this MeasureSpec. Therefore, MeasureSpec LayoutParams View, not only by the determined, and the parent vessel along LayoutParams need to decide View, MeasureSpec, further decides View width / height.

    It should be noted that for the top layer View, which is DecorView and ordinary View, their MeasureSpec conversion process is slightly different:

    DecorView of MeasureSpec conversion process: the size of the window and its own LayoutParams to a common decision.

    Normal View, MeasureSpec conversion process: by the MeasureSpec parent container and its LayoutParams to a common decision.

    MeasureSpec Once determined, onMeasure () method of measurement can be determined View width / height, specifically by the source following analysis MeasureSpec conversion process:

    DecorView of MeasureSpec conversion process:

    1. For DecorView is, measureHierarchy in ViewRootImpl in () method of its measurement process begins:

private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
                                 final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
    int childWidthMeasureSpec;
    int childHeightMeasureSpec;
    boolean goodMeasure = false;
    ...

    if (!goodMeasure) {
        // desiredWindowWidth 和 desiredWindowHeight 是窗口的尺寸,lp 类型为 LayoutParams。
        childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
        childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    ...
    return windowSizeMayChange;
}

    The following look at getRootMeasureSpec () method of internal conversion process:

private static int getRootMeasureSpec(int windowSize, int rootDimension) {
    int measureSpec;

    // 根据 DecorView 的 LayoutParams 中的宽/高参数进行分支
    switch (rootDimension) {
        // 当宽/高设置为 match_parent 时,SpecMode 为 EXACTLY 模式
        case ViewGroup.LayoutParams.MATCH_PARENT:
            // 精确模式,大小就是 windowSize 的大小,也就是窗口的大小。
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
            break;

        // 当宽/高设置为 wrap_content 时,SpecMode 为 AT_MOST 模式
        case ViewGroup.LayoutParams.WRAP_CONTENT:
            // 最大模式,大小是窗口的大小。
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
            break;

        default:
            // 默认为精确模式,大小为 DecorView 的 LayoutParams 中的宽/高
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
            break;
    }
    return measureSpec;
}

    Normal View, MeasureSpec conversion process:

    1. For ordinary View, here's View refers to our general layout of View, its measure is initiated by its parent layout (ViewGroup) come to pass, so we start from the analysis before passing the measure of ViewGroup the process begins which is transmitted to the child View measureChildWithMargins () in which:

protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {

    // 获取子 View 的 LayoutParams
    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

    // 这里就是具体获取 View 的 MeasureSpec 方法
    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);

}

    2. It can be seen first by obtaining sub-View of MeasureSpec getChildMeasureSpec () method, and then the execution MeasureSpec measure according to the sub-sub-View of the View. So let's look at getChildMeasureSpec ():

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {

    // 通过父容器的 MeasureSpec 获取其 SpecMode 和 SpecSize
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);

    /*
    * padding = 父容器的 mPaddingLeft + 父容器的 mPaddingRight + 子 View LayoutParams的 leftMargin + 子 View LayoutParams的 rightMargin + 父容器已经使用的宽度 widthUsed
    * size = 父容器的总大小 - 父容器中已占用的空间大小
    * */
    int size = Math.max(0, specSize - padding);

    int resultSize = 0;
    int resultMode = 0;

    // 根据父容器中 MeasureSpec 中的 SpecMode 进行分支
    switch (specMode) {

        // 当父容器 SpecMode 为精确模式时
        case MeasureSpec.EXACTLY:
            // 再根据子 View 的 LayoutParams 进行判断
            if (childDimension >= 0) {
                // 当子 View 的 LayoutParams 为精确大小时
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;

            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // 当子 View 的 LayoutParams 为 match_parent 时
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;

            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // 当子 View 的 LayoutParams 为 wrap_content 时
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;

            }
            break;

        // 当父容器 SpecMode 为最大模式时
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // 当子 View 的 LayoutParams 为精确大小时
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;

            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // 当子 View 的 LayoutParams 为 match_parent 时
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;

            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // 当子 View 的 LayoutParams 为 wrap_content 时
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;

            }
            break;

        // 当父容器 SpecMode 为 UNSPECIFIED 模式时
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // 当子 View 的 LayoutParams 为精确大小时
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;

            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // 当子 View 的 LayoutParams 为 match_parent 时
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;

            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // 当子 View 的 LayoutParams 为 wrap_content 时
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
    }

    // 返回子 View 的 MeasureSpec
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

    In summary, you can see MeasureSpec creation process requires ordinary View parent LayoutParams MeasureSpec container and the View itself to a common decision.

    Normal View, MeasureSpec create summary chart:

 

Guess you like

Origin blog.csdn.net/yz_cfm/article/details/90722051