MeasureSpec 的分析

作者: shenshizhong | 来源:发表于2017-09-19 00:09 被阅读30次

    文章目录

    • 场景
    • MeasureSpec
    • SpecMode
    • 与 LayoutParams 关系
    • 总结

    场景

    当我们需要了解 View 的原理,我们肯定会碰到这么一个类 MeasureSpec,那么它是干嘛的呢,它有什么作用,在理解 View 原理时,可以跳过它么,它充当什么角色呢?

    MeasureSpec

    • 它是 View类中 的一个静态内部类
    • MeasureSpec 可以理解成测量规范
    • MeasureSpec 通过将 SpecMode 和 SpecSize
      打包成 int 值,来减少本地对象。这个类还提供了打包和解包方法
    • measureSpec 是一个32位 int 值,高2位代表 mode,低30位代表 size。mode 是测量模式,size 是测量模式下的规格大小。
      看下源码中的重要方法(解包,打包):
     public static class MeasureSpec {
            public static int getMode(int measureSpec) {
                //noinspection ResourceType
                return (measureSpec & MODE_MASK);
            }
            public static int getSize(int measureSpec) {
                return (measureSpec & ~MODE_MASK);
            }
            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);
                }
            }
    }
    

    SpecMode

    • 三种模式,分别是:UNSPECIFIED , EXACTLY , AT_MOST
      1、UNSPECIFIED
      View 要多大有多大,不受父容器限制。一般用于系统内部
      2、EXACTLY
      View 的大小等于精确的数值
      3、AT_MOST
      父容器指定了一个可用的大小SpecSize,View 的大小不能大于这个值。

    与 LayoutParams 关系

    • View 的 measureSpec 值,由父容器和 View 的 LayoutParams 一起决定的
    • DecorView 的 measureSpec 值 由窗体大小和 DecorView 的 LayoutParams 决定
    • 普通 View 的 measureSpec 值 由父容器 MeasureSpec 和 View 的 LayoutParams 决定
    一、看下 DecorView 怎么获取 measureSpec :

    1、其中 desiredWindowWidth,desiredWindowHeight 是窗口宽和高的大小。lp.width ,lp.height 是 LayoutParams 的宽高参数,下面是
    ViewRootImpl#measureHierarchy()源码:

       private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
                final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
    ...
                childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
                childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
     return windowSizeMayChange;
    }
    

    2、获取 measureSpec

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

    根据以上代码可以看出,DecorView 根据它的 LayoutParams 中的宽高的参数来划分的,还有窗口大小,最终返回 measureSpec.

    二、普通 view 的怎么获取 measureSpec:

    1、 看下 ViewGroup#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);
        }
    

    2、从上面中我们看到调用了getChildMeasureSpec 方法,所以,我们在看下
    ViewGroup#getChildMeasureSpec 方法源码:

        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);
        }
    

    从中可以发现的,普通 View 的 measureSpec 由父容器和 View 的 LayoutParams 共同决定的。另外在1展示地代码中更有这么一句:child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    从这句代码中,我们也知道了, View 的测量是由 View 的 measureSpec 决定的。

    总结

    • MeasureSpec 是测量规范
    • measureSpec 代表一个 32位 int值,高2位是 mode(测量模式),低30位是 size(测量模式下的大小)
    • DecorView 的 measureSpec 由窗口大小和 DecorView 的 LayoutParams 共同决定
    • 普遍 View 的 measureSpec 由父容器 MeasureSpec 和 View 的 LayoutParams 共同决定
    • View 的测量是由 View 的 measureSpec 决定的

    如果对你有一点点帮助,那是值得高兴的事情。:)
    欢迎长按下图 -> 识别图中二维码 或者 扫一扫 关注我的公众号:

    shenshizhong.png

    我的csdn:http://blog.csdn.net/shenshizhong
    我的简书:http://www.jianshu.com/u/345daf0211ad

    相关文章

      网友评论

        本文标题:MeasureSpec 的分析

        本文链接:https://www.haomeiwen.com/subject/ivubsxtx.html