如何让LaunchedEffect执行指定次数?Jetpack Compose循环动画实现
解决方案:实现点击按钮后动画重复执行3次
问题背景
点击START按钮后,圆形组件需按指定时长完成3次缩放循环动画,原代码无法实现重复执行逻辑。
修改后的完整代码
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ProgressBarTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { BallScaleIndicator() } } } } } @Composable fun BallScaleIndicator() { var isAnimating by remember { mutableStateOf(false) } var animationCycleCount by remember { mutableStateOf(0) } val animationProgressForText by animateFloatAsState( targetValue = if (isAnimating) 1f else 0.0f, animationSpec = keyframes { durationMillis = 12000 // 单次循环时长:放大4s + 保持2s + 缩小4s 0.0f at 0 0.8f at 4000 0.8f at 6000 1f at 12000 }, finishedListener = { // 单次动画结束后,减少循环计数,若还有剩余则重启动画 if (animationCycleCount > 0) { animationCycleCount -= 1 isAnimating = false // 延迟一小段时间后重启,避免状态切换太快导致动画不触发 LaunchedEffect(Unit) { delay(10) isAnimating = true } } else { isAnimating = false } }, label = "textAnimation" ) val animationProgress by animateFloatAsState( targetValue = if (isAnimating) 0.8f else 0.6f, animationSpec = keyframes { durationMillis = 12000 0.6f at 0 0.8f at 4000 0.8f at 6000 0.6f at 12000 }, label = "scaleAnimation" ) Column { Button(onClick = { // 点击开始时重置循环计数并启动动画 animationCycleCount = 3 isAnimating = true }) { Text(text = if (isAnimating) "RUNNING..." else "START") } Ball( modifier = Modifier .size(500.dp) .padding(16.dp) .scale(animationProgress), animationProgress = animationProgressForText ) } } @Composable fun Ball( modifier: Modifier = Modifier, borderColor: Color = Color.Black, borderWidth: Dp = 40.dp, animationProgress : Float ) { Box( modifier = modifier, contentAlignment = Alignment.Center ) { Canvas( modifier = modifier, onDraw = { val canvasSize = size.minDimension val circleRadius = (canvasSize - borderWidth.toPx()) / 2 val circleCenter = Offset(size.width / 2, size.height / 2) drawCircle( color = borderColor, center = circleCenter, radius = circleRadius, style = Stroke(width = borderWidth.toPx()) ) } ) Text( text = when { animationProgress > 0.0 && animationProgress < 0.8 -> "TAKE BREATH" animationProgress in 0.8..0.8000001 -> "HOLD ON" animationProgress == 0.0f -> "START" else -> "GIVE BREATH" }, fontSize = 35.sp, color = Color.Black ) } }
关键修改说明
- 状态管理优化:新增
isAnimating控制动画启停,animationCycleCount记录剩余循环次数,替代原有的isScalingUp单一状态,更清晰控制循环逻辑。 - 动画结束回调:利用
animateFloatAsState的finishedListener,在单次动画完成后自动减少循环计数,若还有剩余次数则重启动画,直到完成3次循环。 - 按钮状态更新:按钮文本根据动画运行状态切换为"RUNNING..."或"START",提升交互体验。
- 移除无效代码:删除原代码中
LaunchedEffect外直接修改isScalingUp = false的错误逻辑,避免动画被强制中断。
内容的提问来源于stack exchange,提问作者Yunus Emrah Uluçay
相关产品推荐
相关产品推荐

