Compose UI
Compose 底层 · androidx.compose.ui
Modifier:有顺序的节点链1.13.0-alpha01
理解 Modifier 顺序、复用和自定义 Modifier.Node 的边界。
最后更新 2026-08-01
顺序会改变结果
// padding 在 clickable 之后:padding 区域也参与点击
Modifier
.clickable(onClick = onClick)
.padding(16.dp)
// 交换顺序:padding 区域不在点击链内
Modifier
.padding(16.dp)
.clickable(onClick = onClick)顺序影响所有阶段:
| 阶段 | 受顺序影响的例子 |
|---|---|
| 布局 | weight vs padding 的先后改变尺寸计算 |
| 绘制 | background vs graphicsLayer 的图层关系 |
| 输入 | clickable vs pointerInput 的命中区域 |
| 语义 | testTag vs contentDescription 的暴露方式 |
原则:布局类(size/padding)放前面,绘制/输入类(background/clickable)放后面,可预测性最好。
Modifier 本身不可变
每次扩展返回新链,安全复用公共前缀:
val itemModifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
Text("A", itemModifier)
Text("B", itemModifier) // 各自独立的链,互不影响scope 约束:依赖特定 scope 的 modifier(weight、align)只能在对应父布局的 scope 内使用,不能抽到外部复用。
条件组合
val modifier = Modifier
.fillMaxWidth()
.then(if (isSelected) Modifier.background(selectedColor) else Modifier)
.then(customModifier)用 .then() 或 if 分支条件拼接;避免用空字符串/魔数控制行为。
自定义扩展还是 Node
| 场景 | 方案 |
|---|---|
| 只组合现有 modifier | fun Modifier.myStyle() = ...(扩展函数) |
| 需要长期状态(缓存测量结果) | ModifierNodeElement + Modifier.Node |
| 高频更新(每帧) | Modifier.Node(避免每帧分配) |
| 参与底层布局/绘制/输入 | Modifier.Node |
业务页面优先组合现有 API;Node 是库作者和性能敏感基础设施的工具。
常见陷阱
- 顺序错误:padding 与 clickable 顺序影响点击区域。
- scope 泄漏:
weight用在错误父布局外编译错误。 - 每帧创建新 modifier:
remember缓存稳定的 modifier 实例。 - 不理解 then():
Modifier.then是拼接的底层实现,条件组合用if+then。
复制即用
@Composable
fun CardItem(
text: String,
selected: Boolean,
onClick: () -> Unit,
) {
val modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.then(
if (selected) {
Modifier.background(MaterialTheme.colorScheme.primaryContainer)
} else {
Modifier
}
)
.clickable(onClick = onClick)
Text(text, modifier)
}要点
- Modifier 链顺序决定布局/绘制/输入/语义的行为。
- 不可变、可复用;scope 相关 modifier 不能跨父布局。
- 组合用扩展函数,需要状态/高频用 Modifier.Node。
- 条件拼接用
if+then。