Compose Foundation
Compose 底层 · androidx.compose.foundation
点击、InteractionSource 与 Indication1.13.0-alpha01
一个可点击组件如何同时表达行为、状态和视觉反馈。
最后更新 2026-08-01
clickable 已经组合了多层能力
Modifier.clickable(
enabled = enabled,
role = Role.Button,
onClick = onClick,
)它不只是监听 pointer,还处理:
- 焦点(键盘可达)
- 语义(TalkBack 读出 Button 角色)
- indication(涟漪反馈)
- 最小点击面积(
minimumInteractiveComponentSize)
普通点击区域优先用 clickable,而不是 pointerInput { detectTapGestures(...) }——后者丢掉语义、焦点和 indication。
观察交互状态
val interactions = remember { MutableInteractionSource() }
val pressed by interactions.collectIsPressedAsState()
Box(
Modifier
.clickable(
interactionSource = interactions,
indication = LocalIndication.current,
onClick = onClick,
)
.background(if (pressed) PressedColor else NormalColor)
) {
Content()
}InteractionSource 发出事件,扩展函数配对成状态:
| 扩展 | 状态 |
|---|---|
collectIsPressedAsState() | 是否按住 |
collectIsFocusedAsState() | 是否聚焦 |
collectIsHoveredAsState() | 是否悬停(桌面) |
collectIsDraggedAsState() | 是否拖拽中 |
关键:组件与外部要观察同一交互,必须共享同一个 source——组件内部新建的 source 外部拿不到。
Indication 只负责视觉
// 自定义指示器(示例:纯色高亮而非涟漪)
class HighlightIndication : Indication {
override fun rememberUpdatedInstance(
interactionSource: InteractionSource,
): IndicationInstance = HighlightInstance(interactionSource)
}
class HighlightInstance(
private val source: InteractionSource,
) : IndicationInstance {
private val pressedColor = Color(0x22000000)
override fun ContentDrawScope.drawIndication() {
drawContent()
val pressed by source.collectIsPressedAsState()
if (pressed) drawRect(pressedColor)
}
}- Indication 读取交互并绘制反馈,不负责 onClick。
- 自定义视觉反馈时保留 clickable 的行为和语义,不要退回原始 pointer handler。
- 主题的
indication通过LocalIndication提供。
常见陷阱
- 用 pointerInput 代替 clickable:丢语义/焦点/indication。
- source 不共享:组件内部监听,外部再建一个——两个 source 互不通知。
- Indication 里触发逻辑:Indication 只画,不处理点击。
- 忘记 enabled 联动:
enabled=false时应该没有 pressed 状态。
复制即用
@Composable
fun PressableCard(
onClick: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
val interactions = remember { MutableInteractionSource() }
val pressed by interactions.collectIsPressedAsState()
Box(
modifier
.clip(RoundedCornerShape(16.dp))
.scale(if (pressed) 0.97f else 1f)
.clickable(
interactionSource = interactions,
indication = null, // 自定义反馈时关掉默认涟漪
onClick = onClick,
),
contentAlignment = Alignment.Center,
) {
content()
}
}要点
- clickable = pointer + 焦点 + 语义 + indication,别退回原始手势。
- InteractionSource 发出事件,collect*AsState 配对成状态;共享 source 才能互见。
- Indication 只负责视觉绘制;自定义反馈保留语义。
- 状态驱动(pressed → 缩放/变色)是最常见的自定义反馈模式。