1 Indication 最常见的使用方式是用来解决解决Compose 按钮点击的水波纹效果
private object NoIndication : Indication {
private object NoIndicationInstance : IndicationInstance {
override fun ContentDrawScope.drawIndication() {
drawContent()
}
}
@Composable
override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
return NoIndicationInstance
}
}
CompositionLocalProvider(
LocalRippleTheme provides NoRippleTheme,
) {}
这个是最常见的代码 取消按钮点击时候的水波纹效果。
这个水波纹的效果依赖于 indication interactionSource 这两个参数。当我们自己不设置时用的是LocalIndication.current
Modifier.clickable(
enabled = enabled,
onClickLabel = onClickLabel,
onClick = onClick,
role = role,
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
)
LocalIndication.current 的默认类型是DefaultDebugIndication
val LocalIndication = staticCompositionLocalOf<Indication> {
DefaultDebugIndication
}
所以我们想取消某个点击的水波纹效果,只需要把 indication设置为null
.clickable(
onClick = {
},
interactionSource = remember { MutableInteractionSource() },
indication = null
),
设置为null之后最终使用的Indication就是NoIndication 也就是1中定义的。
val resolvedIndication = indication ?: NoIndication
2 自定义Indication 实现点击效果
这个是官方代码 点击可以实现被点击组件子节点的放大缩小效果。
object ScaleIndication : Indication {
@Composable
override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
// key the remember against interactionSource, so if it changes we create a new instance
val instance = remember(interactionSource) { ScaleIndicationInstance() }
LaunchedEffect(interactionSource) {
interactionSource.interactions.collectLatest { interaction ->
when (interaction) {
is PressInteraction.Press -> instance.animateToPressed(interaction.pressPosition)
is PressInteraction.Release -> instance.animateToResting()
is PressInteraction.Cancel -> instance.animateToResting()
}
}
}
return instance
}
}
private class ScaleIndicationInstance : IndicationInstance {
var currentPressPosition: Offset = Offset.Zero
val animatedScalePercent = Animatable(1f)
suspend fun animateToPressed(pressPosition: Offset) {
currentPressPosition = pressPosition
animatedScalePercent.animateTo(0.9f, spring())
}
suspend fun animateToResting() {
animatedScalePercent.animateTo(1f, spring())
}
override fun ContentDrawScope.drawIndication() {
scale(
scale = animatedScalePercent.value,
pivot = currentPressPosition
) {
this@drawIndication.drawContent()
}
}
}
3 跟随NoIndication出现的代码 还有这个样板代码,也是用来改变水波纹的颜色。
object NoRippleTheme : RippleTheme {
@Composable
override fun defaultColor(): Color {
return Color.Red
}
@Composable
override fun rippleAlpha(): RippleAlpha {
return RippleAlpha(0f, 0f, 0f, 1f)
}
}
其实使用了 NoIndication 这个 NoRippleTheme就没用了。并且要配合rememberRipple使用RippleTheme设置的属性才能生效。
.clickable(
onClick = {
},
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple()
),
rememberRipple -> PlatformRipple -> Ripple 在Ripple的rememberUpdatedRippleInstance方法中使用了 LocalRippleTheme.current 也就是我们自己设置的 NoRippleTheme。
总结如下:
1 clickable 如果不设置indication用的就是LocalIndication.current 也就是我们通过 CompositionLocalProvider 设置的 LocalIndication
2 clickable 如果设置 indication ,那么点击效果由自己设置的 indication 决定。
3 如果设置为 null 使用的是 NoIndication,点击没有任何效果。
4 LocalRippleTheme只是一个提供属性的类,至于是否会影响到点击效果,就要看设置的indication中有没有使用它。官方定义的rememberRipple()中就使用到了它,所以改变它会影响rememberRipple()的点击效果。
另外新版本的写法稍有改变:
object ScaleIndicationNodeFactory : IndicationNodeFactory {
override fun create(interactionSource: InteractionSource): DelegatableNode {
return ScaleIndicationNode(interactionSource)
}
override fun hashCode(): Int = -1
override fun equals(other: Any?) = other === this
}
class ScaleIndicationNode(
private val interactionSource: InteractionSource
) : Modifier.Node(), DrawModifierNode {
var currentPressPosition: Offset = Offset.Zero
val animatedScalePercent = Animatable(1f)
private suspend fun animateToPressed(pressPosition: Offset) {
currentPressPosition = pressPosition
animatedScalePercent.animateTo(0.9f, spring())
}
private suspend fun animateToResting() {
animatedScalePercent.animateTo(1f, spring())
}
override fun onAttach() {
coroutineScope.launch {
interactionSource.interactions.collectLatest { interaction ->
when (interaction) {
is PressInteraction.Press -> animateToPressed(interaction.pressPosition)
is PressInteraction.Release -> animateToResting()
is PressInteraction.Cancel -> animateToResting()
}
}
}
}
override fun ContentDrawScope.draw() {
scale(
scale = animatedScalePercent.value,
pivot = currentPressPosition
) {
this@draw.drawContent()
}
}
}