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值在某种测量模式下的规格大小。

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。

 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

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

这里我举一个列子,比方说我们在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都是充满了整个屏幕

 
    
   
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/202058.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月20日 上午7:59
下一篇 2026年3月20日 上午7:59


相关推荐

发表回复

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

关注全栈程序员社区公众号