Jetpack Compose:如何让LazyRow项仅在页面启动时执行缩放入场动画
解决Jetpack Compose LazyRow项动画仅页面启动执行一次的问题
问题核心在于LazyRow的列表项会在滚出屏幕时被移出Compose组合树,滚回时重新进入组合,导致原代码中LaunchedEffect(true)会重复触发动画。我们可以通过在页面级维护已执行动画的项ID集合,来避免重复播放。
修改步骤
1. 父组件中添加动画状态跟踪集合
在ChooseUI中创建一个MutableSet来记录已经播放过动画的项ID,这个集合和页面生命周期绑定,不会因为列表滚动而丢失状态:
@Composable fun ChooseUI( modifier: Modifier, navAction: () -> Unit, count: Int ) { // 记录已完成动画的项ID,和页面生命周期绑定 val animatedIds = remember { mutableStateSetOf<Int>() } ... LazyRow( modifier = modifier, contentPadding = PaddingValues(horizontal = 8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { items(count = count, key = { it }) { controlId -> CustomControlCard( controlId = controlId, modifier = modifier, navAction = {}, packageName = packageName, resourceFolder = resourceFolder, animatedIds = animatedIds // 传递状态集合给子组件 ) } } } }
2. 子组件中控制动画触发逻辑
修改CustomControlCard,仅在项首次出现且未执行过动画时播放动画,同时设置正确的初始缩放值避免视觉闪烁:
@SuppressLint("DiscouragedApi") @OptIn(ExperimentalMaterial3Api::class) @Composable private fun CustomControlCard( modifier: Modifier, navAction: (String) -> Unit, controlId: Int, packageName: String, resourceFolder: Resources, animatedIds: MutableSet<Int> // 新增参数:接收已动画项ID集合 ) { ... // 根据是否已执行动画设置初始缩放值,避免重复进入时的闪烁 val scale = remember(controlId, animatedIds) { Animatable(if (controlId in animatedIds) 1f else 0f) } LaunchedEffect(controlId) { // 仅当项未执行过动画时,才播放入场动画 if (controlId !in animatedIds) { scale.animateTo( targetValue = 1f, animationSpec = tween( durationMillis = 250, delayMillis = 100 * controlId, easing = LinearEasing ) ) // 标记该项已完成动画 animatedIds.add(controlId) } } Card( modifier = modifier .size(width = 110.dp, height = 200.dp) .scale(scale = scale.value), onClick = { } ) { Box( modifier = modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.surface), contentAlignment = Alignment.BottomCenter, ) { Image( painter = painterResource(id = drawableId), contentDescription = null, alignment = Alignment.Center, modifier = Modifier .padding(5.dp) .fillMaxSize() ) } } }
原理说明
- 父组件的
animatedIds集合通过remember绑定页面生命周期,确保页面销毁前状态不会丢失。 - 每个列表项首次进入组合时,检查自身ID是否在
animatedIds中,未存在则执行动画并标记已完成。 - 当项滚出再滚回时,由于ID已在集合中,动画不会重复触发,同时初始缩放值直接设为1f,避免视觉异常。
内容的提问来源于stack exchange,提问作者StackerSapper
相关产品推荐
相关产品推荐

