子 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 被压小了"。

相关推荐
AFinalStone1 小时前
Android 7 多用户源码解析(七)系统服务的多用户感知处理
android·系统架构·多用户
怪奇云呼军1 小时前
从 ElevenLabs 看工具调用:闪电智能 Voice Agent 的企业集成验收设计
android·大数据·运维·服务器·网络·人工智能·kotlin
157092511342 小时前
Android 架构模式详解:从 MVC、MVP 到 MVVM
android·架构·mvc
aaajj3 小时前
Android手机连接鼠标情况
android
其实防守也摸鱼4 小时前
SQL注入实验笔记
android·运维·数据库·安全·oracle·自动化
找茬小王子4 小时前
Android原生Monkey工具的使用
android·monkey
Code Man4 小时前
实现 Android 字符设备驱动
android·驱动开发·lineageos
传奇开心果编程5 小时前
【Flutter入门练中学】第3课:滚动与列表
android·学习·flutter·ui·ios
JosieBook5 小时前
【数据库】MySQL 实战精通系列 · 第4篇:索引原理与执行计划实战
android·数据库·mysql