我想在 Compose 中制作一个这样的按钮: https://pub.dev/packages/flutter_bounceable
但是可点击的方法在我的代码中不起作用。
我尝试使用此代码,但有错误。 按下按钮,但没有任何动作。 动画效果很好,但不适用于可点击。
fun Modifier.bounceClick(onClick: () -> Unit,animationDuration: Int = 100,
scaleDown: Float = 0.9f) = composed {
val interactionSource = MutableInteractionSource()
val coroutineScope = rememberCoroutineScope()
val scale = remember {
Animatable(1f)
}
this
.scale(scale = scale.value)
.background(
color = Color(0xFF35898F),
shape = RoundedCornerShape(size = 12f)
)
.clickable(interactionSource = interactionSource, indication = null, onClick = onClick)
.pointerInput(Unit) {
while(true)
awaitPointerEventScope {
awaitFirstDown()
coroutineScope.launch {
scale.animateTo(
scaleDown,
animationSpec = tween(animationDuration),
)
}
waitForUpOrCancellation()
coroutineScope.launch {
scale.animateTo(
scaleDown,
animationSpec = tween(20),
)
scale.animateTo(
1f,
animationSpec = tween(animationDuration),
)
}
}
}
}
使用 Compose 可以非常简单地做到这一点。
如果 Compose 版本为 1.4.0-alpha03,则应使用 foreachGesture 或 waitEachGesture 并使用 Modifier.pointerInput 而不是 while。另外,当你有可点击的功能时,你也不需要 Modifier.pointerInput ,你可以使用它们中的任何一个。
我只会演示如何使用
Modifier.clickable
和 interactionSource.collectIsPressedAsState()
进行操作,如下所示。
结果
实施
fun Modifier.bounceClick(
animationDuration: Int = 100,
scaleDown: Float = 0.9f,
onClick: () -> Unit
) = composed {
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()
val animatable = remember {
Animatable(1f)
}
LaunchedEffect(key1 = isPressed) {
if (isPressed) {
animatable.animateTo(scaleDown)
} else animatable.animateTo(1f)
}
Modifier
.graphicsLayer {
val scale = animatable.value
scaleX = scale
scaleY = scale
}
.clickable(
interactionSource = interactionSource,
indication = null
) {
onClick()
}
}
使用方法
@Composable
private fun BounceExample() {
Row {
Box(
Modifier
.background(Color.Red, RoundedCornerShape(10.dp))
.bounceClick {
}
.padding(10.dp),
contentAlignment = Alignment.Center
) {
Text(text = "Hello World", color = Color.White, fontSize = 20.sp)
}
Spacer(modifier = Modifier.width(10.dp))
Box(
Modifier
.bounceClick {
}
.background(Color.Green, RoundedCornerShape(10.dp))
.padding(10.dp),
contentAlignment = Alignment.Center
) {
Text(text = "Hello World", color = Color.White, fontSize = 20.sp)
}
}
}
@Thracian 的回答有一个小问题。如果按钮位于可滚动容器中,并且您快速点击按钮,它将不会产生动画效果。这是因为 isPressed 状态由于内置延迟而不会调度(位于
Clickable.kt
撰写文件中)。
要修复它,请将其更改为以下内容:
private fun Modifier.bounceClick(
scaleDown: Float = 0.90f,
onClick: () -> Unit
) = composed {
val interactionSource = remember { MutableInteractionSource() }
val animatable = remember {
Animatable(1f)
}
LaunchedEffect(interactionSource) {
interactionSource.interactions.collect { interaction ->
when (interaction) {
is PressInteraction.Press -> animatable.animateTo(scaleDown)
is PressInteraction.Release -> animatable.animateTo(1f)
is PressInteraction.Cancel -> animatable.animateTo(1f)
}
}
}
Modifier
.graphicsLayer {
val scale = animatable.value
scaleX = scale
scaleY = scale
}
.clickable(
interactionSource = interactionSource,
indication = null
) {
onClick()
}
}