Jetpack Compose 会在不同阶段执行修饰符。以下细分显示了常见修饰符属于哪个阶段,以及何时对其参数或 lambda 进行求值。
第 1 阶段:组合阶段
这些修饰符会在 @Composable 主体内同步评估实参。在此处读取的任何状态都会向可组合项重启范围注册依赖项,并在发生更改时触发完整重组。
- 静态尺寸和尺寸修饰符:
Modifier.size(dp)、Modifier.width(dp)、Modifier.height(dp)Modifier.fillMaxSize()、Modifier.fillMaxWidth()、Modifier.fillMaxHeight()Modifier.wrapContentSize()、Modifier.wrapContentWidth()、Modifier.wrapContentHeight()Modifier.requiredSize()、Modifier.defaultMinSize()Modifier.aspectRatio(ratio)Modifier.weight(weight)(RowScope或ColumnScope)Modifier.width(IntrinsicSize.Min / Max)Modifier.height(IntrinsicSize.Min / Max)
- 静态间距和边衬区:
Modifier.padding(all = dp)、Modifier.padding(start, top, end, bottom)Modifier.windowInsetsPadding(insets)、Modifier.statusBarsPadding()
- 直接值视觉修饰符:
Modifier.alpha(float)(在组合期间读取状态)Modifier.rotate(degrees)(在组合期间读取状态)Modifier.scale(scaleX, scaleY)(在组合期间读取状态)Modifier.background(color = Color, shape = Shape)Modifier.border(width, color, shape)Modifier.shadow(elevation, shape)Modifier.clip(shape)Modifier.blur(radius)
- 静态位置修饰符:
Modifier.offset(x = dp, y = dp)(直接值重载)Modifier.absoluteOffset(x = dp, y = dp)
- 动态可组合项修饰符:
Modifier.composed { ... }(在组合期间运行工厂主体;不建议用于性能 - 请改用Modifier.Node作为自定义修饰符)
第 2 阶段:布局阶段
布局阶段分为两个不同的子步骤,每个子步骤都有各自的失效范围:
第 2A 步:衡量
确定大小(width、height)和约束条件。此处的状态读取会触发重新评测和重新放置:
Modifier.layout { measurable, constraints -> ... }:layout(w, h)之前的代码在衡量期间执行。
第 2B 步:展示位置和布局回调
确定 (x, y) 坐标位置。在放置块内读取状态只会触发重新放置(跳过测量和组合):
Modifier.offset { IntOffset(x, y) }(lambda 重载)Modifier.absoluteOffset { IntOffset(x, y) }(lambda 重载)Modifier.layout { ... layout(w, h) { placeable.place(x, y) } }:layout(w, h) { ... }块内的代码。
布局观察回调也会在布局传递期间或之后运行(避免在这些回调中写入在 Composition 中读取的状态,因为这样做会导致向后写入):
Modifier.onSizeChanged { intSize -> ... }:如果节点的尺寸发生变化,则在布局测量完成后调用。Modifier.onPlaced { layoutCoordinates -> ... }和Modifier.onLayoutRectChanged { ... }:在放置后调用,用于报告局部坐标或边界,而无需订阅全局窗口布局传递。Modifier.onGloballyPositioned { layoutCoordinates -> ... }:在任何节点移动时,在整个窗口布局传递完成后调用。尽可能避免使用onGloballyPositioned;最好使用Modifier.onVisibilityChanged进行视口跟踪,使用Modifier.onPlaced或Modifier.onLayoutRectChanged获取局部边界,或在Modifier.Node中按需使用requireLayoutCoordinates()。
第 3 阶段:抽签阶段
这些修饰符会在画布绘制期间执行。在这些代码块内读取的状态会同时跳过 Composition 和 Layout,仅运行绘制阶段:
Modifier.graphicsLayer { ... }:- 在 lambda 中更新的属性:
alpha、translationX、translationY、scaleX、scaleY、rotationX、rotationY、rotationZ、shadowElevation、shape、clip、renderEffect、transformOrigin。
- 在 lambda 中更新的属性:
Modifier.drawBehind { ... }:- 在可组合项内容后面绘制画布命令。
Modifier.drawWithContent { ... }:- 使用
drawContent()控制绘制顺序。
- 使用
Modifier.drawWithCache { ... }:- 外部代码块在大小或布局发生变化时运行,以缓存
Path、Paint或Brush对象;onDrawBehind或onDrawWithContent在每个帧绘制时运行。
- 外部代码块在大小或布局发生变化时运行,以缓存
在重组循环之外 - 观察修饰符
某些修饰符在三个帧阶段之外运行,用于处理无障碍功能、用户输入和生命周期观察。
语义和无障碍功能
为无障碍服务 (TalkBack) 和测试处理语义属性:
Modifier.semantics { ... }Modifier.clearAndSetSemantics { ... }
输入、手势和事件
在三阶段帧渲染流水线之外运行,接收操作系统动作事件和用户互动:
Modifier.pointerInput(key) { detectTapGestures { ... } }/detectDragGestures/awaitPointerEventScopeModifier.clickable(enabled, onClickLabel) { ... }Modifier.combinedClickable { ... }Modifier.toggleable { ... }Modifier.selectable { ... }Modifier.draggable(...)/Modifier.scrollable(...)/Modifier.draggable2D(...)Modifier.onFocusChanged { focusState -> ... }Modifier.onKeyEvent { keyEvent -> ... }Modifier.onVisibilityChanged { isVisible -> ... }:在协程中,当可组合项的可见性在配置的延迟时间后发生变化时,系统会调用该回调。
快速参考:价值与 Lambda 阶段
在 lambda 中读取状态值会将读取操作推迟到后续阶段,从而在状态发生变化时跳过前面的阶段:
| 修饰符 / 模式 | 直接价值(组合) | Lambda 变体(延迟阶段) | 当状态发生变化时 |
|---|---|---|---|
| 偏移值 | Modifier.offset(x, y) |
Modifier.offset { IntOffset(x, y) } |
布局(放置)(跳过组合) |
| Alpha 值 / 不透明度 | Modifier.alpha(alpha) |
Modifier.graphicsLayer { this.alpha = alpha } |
绘制(跳过组合和布局) |
| 旋转 | Modifier.rotate(degrees) |
Modifier.graphicsLayer { rotationZ = degrees } |
绘制(跳过组合和布局) |
| 发展壮大 | Modifier.scale(scale) |
Modifier.graphicsLayer { scaleX = s; scaleY = s } |
绘制(跳过组合和布局) |
| 翻译 | Modifier.padding(...) |
Modifier.graphicsLayer { translationX = ... } |
绘制(跳过组合和布局) |
| 背景颜色 | Modifier.background(color) |
Modifier.drawBehind { drawRect(color) } |
绘制(跳过组合和布局) |
| 滚动 / 视差 | Modifier.offset(scroll.dp) |
Modifier.offset { IntOffset(0, scroll) } |
布局(放置)(跳过组合) |
Modifier.Node 接口映射
对于使用高性能 Modifier.Node API 的自定义修饰符,各个阶段会直接映射到节点功能接口:
| 阶段 | Modifier.Node 接口 |
核心方法 |
|---|---|---|
| 布局(测量和放置) | LayoutModifierNode |
measure(measurable, constraints) |
| 布局(全局坐标) | GlobalPositionAwareModifierNode (首选按需 requireLayoutCoordinates()) |
onGloballyPositioned(coordinates) |
| 布局(展示位置) | LayoutAwareModifierNode |
onRemeasured(size)、onPlaced(coordinates) |
| 绘图 | DrawModifierNode |
draw(ContentDrawScope) |
| 输入 / 指针 | PointerInputModifierNode |
onPointerEvent(...)、onCancelPointerInput() |
| 语义 | SemanticsModifierNode |
applySemantics(SemanticsPropertyReceiver) |
| 焦点 | FocusEventModifierNode |
onFocusEvent(focusState) |