Android MeasureSpec理解

Android MeasureSpec理解MeasureSpec字面意思为测量的规格,他决定了view的测量过程接下来探讨几个问题来学习MeauseSpec1.MeasureSpec的构成2.如何创建MeasureSpec3.默认View的OnMeasure过程中对MeasureSpec的处理一.MeasureSpec的构成M…

MeasureSpec字面意思为测量的规格,他决定了view的测量过程

接下来探讨几个问题来学习MeauseSpec
1. MeasureSpec的构成
2. 如何创建MeasureSpec
3. 默认View的OnMeasure过程中对MeasureSpec的处理

一.MeasureSpec的构成

MeasureSpec代表一个32位的int值,前俩位代表SpecMode,后30位代表SpecSize.其中:SpecMode代表测量的模式,SpecSize值在某种测量模式下的规格大小。

共有三种测量模式:
1. EXACTLY: 父容器已经检测出子View所需要的精确大小,这个时候view的大小即为SpecSize的大小,他对应于布局参数中的MATCH_PARENT,或者精确大小值

2.AT_MOST: 父容器指定了一个大小,即SpecSize,子view的大小不能超过这个SpecSize的大小

3.UNSPECIFIED: 表示子View想多大都可以

二.如何创建MeasureSpec

MeasureSpec内部提供了创建MeasureSpec的方法:

public static int makeMeasureSpec(int size, int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

MeasureSpec代表一个32位的int值,前俩位代表SpecMode,后30位代表SpecSize.通过巧妙的位运算,即可通过MeasureSpec来得到SpecSize,SpecMode.

public static int getMode(int measureSpec) {
            return (measureSpec & MODE_MASK);
        }
   
   
   
   
  • 1
  • 2
  • 3
public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }
   
   
   
   
  • 1
  • 2
  • 3

系统内部是通过MeasureSpec来对view进行测量。在view测量的时候,系统会将LayoutParams在父容器的约束下创建MeasureSpec.

view的顶级View为DecorView.他的MeasureSpec有窗口的尺寸与布局参数来决定。对于普通的View,他的MesureSpec由父容器的MeasureSpec与自身的布局参数来一起决定的。

DecorView的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;
    }
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

其中windowSize为窗口的大小,即屏幕大小,rootDimension在DecorView中为MATCH_PARENT.故DecorView的MeasureSpec中的SpecSize为窗口大小,SpecMode的EXACTLY.

对于普通的子View来说,通过传入父容器的MeasureSpec,来计算子View的MeasureSpec。

通过measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed);
在其内部在调用getChildMeasureSpec()方法来得到MeasureSpec.

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

大体过程如下:
一:如果父容器的SpecMode为EXACTLY,那么在判断子view自己的布局参数.
1.如果为具体数值,或者MATCH_PARENT时候,那么子view的SpecMode为EXACTLY,SpecSize为父容器的SpecSize,为具体指时,SpecSize为具体值。
2.如果为WRAP_CONTENT时,那么子View的SpecMode为AT_MOST,SpecSize为父容器的SpecSize.表示子View最大不会超过父容器

二:如果父容器的SpecMode为AT_MOST时:
1.如果子View的测量规格为MATCH_PARENT,或者WRAP_CONTENT时,那么子View的SpecMode为AT_MOST,SpecSize为父容器的SpecSize.

2.如果子View的测量规格为精确值时,那么子View的测量规格为EXACTLY,SpecSize为具体的值。

上述的判断过程均为系统的判断过程,了解这些有益于得知开发过程中出现的情况的原因。

三. 默认View的OnMeasure过程中对MeasureSpec的处理

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
   
   
   
   
  • 1
  • 2
  • 3
  • 4

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

从上面的代码中可以看到默认onMeasure()方法中的判断逻辑为:
在SpecMode为AT_MOST,EXACTLY时,测量的大小都为SpecSize.

这里我举一个列子,比方说我们在activity的布局文件中自定义了一个View,把这个view的宽高都设置为WRAP_CONTNET时,这时我们会发现这个view充满了整个屏幕。发生这样的原因是正是上面代码逻辑判断的结果。首先这个字View的父容器为DecorView中的一个ViewGroup,他内部是有一个FrameLayout的布局。这个父容器的SpecSize为整个屏幕的大小。现在看子View,子view的布局参数为wrap_content,那么子View的MeasureSpec中的SpecSize为整个屏幕,SpecMode为AT_MOST,在getDefaultSize中,当测量模式AT_MOST,或者MATCH_PARENT时,都是直接把SpecSize的值给返回了,而这个SpecSize的值正好是整个屏幕,所以出现是view不管是设置为MATCH_PARENT,还是WRAP_CONTENT都是充满了整个屏幕

        <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/markdown_views-ea0013b516.css">
            </div>

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/36458.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注