各阶段的修饰符细分

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。
  • Modifier.drawBehind { ... }:
    • 在可组合项内容后面绘制画布命令。
  • Modifier.drawWithContent { ... }:
    • 使用 drawContent() 控制绘制顺序。
  • Modifier.drawWithCache { ... }:
    • 外部代码块在大小或布局发生变化时运行,以缓存 Path、Paint 或 Brush 对象;onDrawBehind 或 onDrawWithContent 在每个帧绘制时运行。

在重组循环之外 - 观察修饰符

某些修饰符在三个帧阶段之外运行,用于处理无障碍功能、用户输入和生命周期观察。

语义和无障碍功能

为无障碍服务 (TalkBack) 和测试处理语义属性:

  • Modifier.semantics { ... }
  • Modifier.clearAndSetSemantics { ... }

输入、手势和事件

在三阶段帧渲染流水线之外运行,接收操作系统动作事件和用户互动:

  • Modifier.pointerInput(key) { detectTapGestures { ... } } / detectDragGestures / awaitPointerEventScope
  • Modifier.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)