You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose:无animate*AsState且避免重组实现LazyColumn卡片批量缩放动画

解决方案

要解决LazyColumn中卡片选择模式的动画性能问题,核心思路是共享动画状态+避免布局级别的变换,具体实现如下:

优化方案说明

  1. 将动画状态提升至父组件,让所有卡片共享同一个动画值,避免每个卡片创建独立的animateFloatAsState实例导致的批量重组。
  2. 使用Modifier.scale()代替fillMaxWidth()实现缩放:scale属于绘制阶段的变换,不会触发布局重测,性能远优于修改宽度的布局变换。
  3. 为LazyColumn的条目设置唯一Key,确保仅可见条目参与更新,不可见条目不会无意义重组。
  4. 稳定传递点击事件lambda,避免因lambda实例变化触发子组件重组。

示例代码

父组件:管理共享动画状态

@Composable
fun SelectableCardList() {
    val items = List(100) { "Item #$it" }
    var selectionMode by remember { mutableStateOf(false) }
    
    // 使用updateTransition管理选择模式的状态切换动画
    val transition = updateTransition(selectionMode, label = "selection_mode_transition")
    val scaleFactor by transition.animateFloat(
        transitionSpec = { tween(300) },
        label = "card_scale"
    ) { isInSelectionMode ->
        if (isInSelectionMode) 0.9f else 1f
    }

    // 稳定点击事件lambda,避免子组件因lambda实例变化重组
    val onCardLongClick = remember {
        { selectionMode = true }
    }

    LazyColumn {
        // 为每个条目设置唯一Key,优化LazyColumn重组行为
        items(items, key = { it }) { itemText ->
            SelectableCard(
                text = itemText,
                isInSelectionMode = selectionMode,
                scaleFactor = scaleFactor,
                onLongClick = onCardLongClick
            )
        }
    }
}

子组件:卡片实现

@Composable
fun SelectableCard(
    text: String,
    isInSelectionMode: Boolean,
    scaleFactor: Float,
    onLongClick: () -> Unit
) {
    Card(
        modifier = Modifier
            .padding(8.dp)
            // 应用共享的缩放动画值,仅触发绘制变换,无布局开销
            .scale(scaleFactor)
            .combinedClickable(
                onClick = {},
                onLongClick = onLongClick
            )
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(100.dp)
                .padding(8.dp)
        ) {
            Text(
                text = text,
                modifier = Modifier.align(Alignment.Center)
            )

            AnimatedVisibility(
                visible = isInSelectionMode,
                enter = slideInHorizontally(),
                exit = slideOutHorizontally(),
                modifier = Modifier
                    .align(Alignment.TopStart)
                    .padding(8.dp)
            ) {
                Icon(
                    imageVector = Icons.Default.CheckCircle,
                    contentDescription = "Selected"
                )
            }
        }
    }
}

关键优化点解析

  • 共享动画状态:updateTransition将选择模式的状态切换与动画绑定,所有卡片复用同一个动画值,避免了大量独立动画实例的创建开销。
  • 绘制级变换:Modifier.scale()仅在绘制阶段缩放卡片,不会改变布局尺寸,因此不会触发布局重测和重组,性能远优于fillMaxWidth()。
  • 稳定lambda:用remember包裹onCardLongClick,确保每次父组件重组时传递给子组件的lambda实例不变,避免子组件无意义重组。
  • LazyColumn Key优化:为每个条目设置唯一Key,LazyColumn会仅更新可见条目的UI,不可见条目不会参与动画和重组。

内容的提问来源于stack exchange,提问作者Ahmad Njres

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 01:35:55