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

如何避免UI小更新触发Crossfade过渡,保留列表项动画?

解决方案

问题根源在于ListUiState.Ready是data class,每次列表项更新(比如删除item)都会生成新的Ready实例,导致Crossfade的targetState频繁变化,触发整个组件的淡入淡出动画,中断LazyColumn的animateItemPlacement动画。我们需要让Crossfade仅在状态类型切换(Loading ↔ Ready ↔ Empty)时触发,而忽略Ready状态内部的列表数据变化。

实现步骤

  1. 定义状态类型枚举,提取状态的类型标识,用于判断是否需要触发Crossfade动画
  2. 修改Crossfade的目标状态,使用状态类型而非完整的ListUiState作为targetState
  3. 在Crossfade内容中复用最新状态数据,确保Ready状态下能获取最新的列表项

修改后的代码

新增状态类型枚举

enum class ListStateType {
    Loading, Ready, Empty
}

更新ListScreen组件

@Composable
fun ListScreen(
    uiState: ListUiState,
    onDeleteItem: (itemId: Long) -> Unit,
) {
    // 提取状态类型,仅当类型变化时触发Crossfade
    val stateType = when(uiState) {
        ListUiState.Loading -> ListStateType.Loading
        is ListUiState.Ready -> ListStateType.Ready
        ListUiState.Empty -> ListStateType.Empty
    }

    Crossfade(
        targetState = stateType,
        label = "List state transition" // 可选,用于动画调试的标签
    ) { currentType ->
        when(currentType) {
            ListStateType.Loading -> CircularProgressIndicator()
            ListStateType.Ready -> {
                // 直接使用外部的uiState获取最新列表项,避免Crossfade中断内部动画
                val readyState = uiState as ListUiState.Ready
                ItemList(
                    items = readyState.items,
                    onDeleteItem = onDeleteItem,
                )
            }
            ListStateType.Empty -> EmptyListView()
        }
    }
}

修正ItemList的参数笔误(原代码中的小问题)

原代码中存在参数名不匹配的问题,修正后:

@Composable
private fun ItemList(
    items: List<Item>,
    onDeleteItem: (itemId: Long) -> Unit,
) {
    LazyColumn(
        verticalArrangement = Arrangement.spacedBy(16.dp),
        contentPadding = PaddingValues(16.dp),
    ) {
        items(
            items,
            key = { it.id },
        ) { item ->
            SwipableItemCard(
                item = item,
                onItemDelete = onDeleteItem,
                modifier = Modifier.animateItemPlacement()
            )
        }
    }
}

原理说明

  • Crossfade现在仅在stateType变化时触发动画,也就是只有在Loading/Ready/Empty三种状态之间切换时才会淡入淡出
  • 当Ready状态下的列表项更新时,stateType保持不变,Crossfade不会重新渲染内部组件,ItemList会直接接收最新的items,LazyColumn的animateItemPlacement动画可以正常运行,不会被中断
  • 通过直接复用外部的uiState,确保Ready状态下始终能获取到最新的列表数据

内容的提问来源于stack exchange,提问作者James Olrog

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 18:14:56