一句话结论:MEASURED_STATE_TOO_SMALL = 0x01000000,它占用测量结果高 8 位中的最低位,用来标记"子 View 想要的尺寸比父容器给的多"。
一、这个常量本身
arduino
// View.java
public static final int MEASURED_STATE_TOO_SMALL = 0x01000000;
把它写成二进制:
yaml
0000 0001 0000 0000 0000 0000 0000 0000
↑
第 24 位(从 0 开始数)
它位于整个 int 的高 8 位区域里:
SSSS SSSS XXXX XXXX XXXX XXXX XXXX XXXX
↑
MEASURED_STATE_TOO_SMALL 就在这里
高 8 位是 state,低 24 位是 size。MEASURED_STATE_TOO_SMALL 就是 state 中的一种具体状态。
二、什么时候会被置位
在 View.resolveSizeAndState() 中,只有当父容器给的 specSize 小于子 View 自己想要的尺寸时,才会触发:
arduino
public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
final int specMode = MeasureSpec.getMode(measureSpec);
final int specSize = MeasureSpec.getSize(measureSpec);
final int result;
switch (specMode) {
case MeasureSpec.AT_MOST:
if (specSize < size) {
// 父容器给的空间不够用,置位警告
result = specSize | MEASURED_STATE_TOO_SMALL;
} else {
result = size;
}
break;
// EXACTLY / UNSPECIFIED 省略
default:
result = size;
}
return result | (childMeasuredState & MEASURED_STATE_MASK);
}
2.1 条件拆解
触发 MEASURED_STATE_TOO_SMALL 必须同时满足:
specMode == AT_MOSTspecSize < childDesiredSize
EXACTLY 模式下父容器说多少就是多少,子 View 没有"不够用"的概念;UNSPECIFIED 模式下没有上限,自然也不会出现不够用。
三、位运算过程
假设父容器只给 300 像素,但子 View 想要 500 像素:
ini
int specSize = 300; // 0x0000012C
int result = specSize | MEASURED_STATE_TOO_SMALL;
二进制展开:
yaml
specSize: 0000 0000 0000 0000 0000 0001 0010 1100
MEASURED_STATE_TOO_SMALL: 0000 0001 0000 0000 0000 0000 0000 0000
---------------------------------------------------------------
result: 0000 0001 0000 0000 0000 0001 0010 1100
↑↑↑↑ ↑↑↑↑
state
↑
size = 300
最终 result = 0x0100012C。高 8 位是 0x01,低 24 位是 0x00012C(即 300)。
四、怎么读取这个状态
MEASURED_STATE_TOO_SMALL 只是高 8 位中的一种状态,读取方式有两种。
4.1 直接判断
ini
boolean tooSmall = (measuredWidth & MEASURED_STATE_TOO_SMALL) != 0;
因为 MEASURED_STATE_TOO_SMALL 只有一位是 1,所以与运算结果要么等于 0x01000000,要么等于 0。
4.2 先取高 8 位再判断
ini
int state = measuredWidth & MEASURED_STATE_MASK; // 0xff000000
boolean tooSmall = (state & MEASURED_STATE_TOO_SMALL) != 0;
这种写法更规范,因为高 8 位未来可能扩展其他状态位。
4.3 取实际尺寸
arduino
int size = measuredWidth & MEASURED_SIZE_MASK; // 0x00ffffff
getMeasuredWidth() 内部就是这条掩码操作,所以日常取尺寸时不会受状态位影响。
五、这个状态位到底有什么用
getMeasuredWidth() 会自动屏蔽高 8 位,所以普通布局逻辑根本感知不到 MEASURED_STATE_TOO_SMALL。它的存在是给父容器做二次决策的:
- 自定义
ViewGroup可以根据这个标志,决定是否为子 View 换行、缩放或滚动 ScrollView等可滚动容器,可以借助这个标志判断内容是否被压小View.getMeasuredWidthAndState()一次性返回完整的 int,父容器可以同时拿到尺寸和状态
ini
int full = view.getMeasuredWidthAndState();
int size = full & MEASURED_SIZE_MASK;
boolean tooSmall = (full & MEASURED_STATE_TOO_SMALL) != 0;
六、总结
| 项目 | 内容 |
|---|---|
| 常量值 | 0x01000000 |
| 所在位置 | int 的第 24 位,高 8 位 state 区域的最低位 |
| 触发条件 | specMode == AT_MOST 且 specSize < childDesiredSize |
| 设置方式 | `specSize |
| 读取方式 | measuredWidth & MEASURED_STATE_TOO_SMALL |
| 尺寸提取 | measuredWidth & MEASURED_SIZE_MASK |
MEASURED_STATE_TOO_SMALL 的本质:一个放在高 8 位的二进制标志位,告诉父容器"子 View 被压小了"。