子 View 被压小时,如何向父容器"告状"?——MEASURED_STATE_TOO_SMALL 向上传递机制

一句话结论: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_MOST
  • specSize < 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 被压小了"。

相关推荐
恋猫de小郭3 分钟前
Dart 3.15 新特性:this 终于支持类型提升了
android·前端·flutter
ii_best31 分钟前
ios手机开发安卓脚本开发软件[按键精灵教程]从「找最小值」到「Boss 最近刷新」再到「屏幕范围极值」
android·ios·智能手机·自动化·按键精灵
开开心心就好1 小时前
安卓PDF处理工具,免费无广告零弹窗
android·服务器·开发语言·算法·pdf·ocr
墨狂之逸才1 小时前
扫码枪响了,应用却没收到:一次 Android USB 线材问题排查
android
alexhilton13 小时前
Room Fts4:全文搜索替代 SQL LIKE
android·kotlin·android jetpack
hai_android18 小时前
把 Android App 打包成系统应用(System App)
android·kotlin·android jetpack
JMchen18 小时前
【Jetpack 内核实战 01】开篇:Jetpack 组件全家桶核心逻辑与专栏完整规划
android·kotlin·android jetpack
骑着蜗牛撵大象32720 小时前
Linux 进程间通信之管道:从 pipe() 的内核语义到 FIFO 落地排错
android·java·linux·系统编程·fifo·管道·进程间通信
hunterandroid21 小时前
Retrofit 网络请求的异常处理与重试策略实战
android
sejinan21 小时前
极致轻量与秒级起播:NativeWasmTv 开源硬核技术解析与升级版体验
android·开源·webview·tv