首页 > 解决方案 > AnimatedVisibility 和 SwipeToDismiss Enter 动画不会触发 - Jetpack Compose

问题描述

好的,所以我一直在尝试在我的应用程序中实现滑动删除功能。每当我从列表中滑动其中一个项目时,我都能看到后面的 RedBackground 并且一切正常。我删除项目时的滑动动画也被成功触发。(即使我不确定为此使用延迟是否是个好主意?我想不出任何其他方式来做到这一点)。但是当我将项目添加到数据库/列表时的输入动画不起作用,我不知道为什么。这是我的懒惰专栏的代码

@Composable
fun DisplayTasks(
    tasks: List<ToDoTask>,
    onSwipeToDelete: (Action, ToDoTask) -> Unit,
    navigateToTaskScreen: (Int) -> Unit
) {
    LazyColumn {
        items(
            items = tasks,
            key = { task ->
                task.id
            }
        ) { task ->
            val dismissState = rememberDismissState()
            val dismissDirection = dismissState.dismissDirection
            val isDismissed = dismissState.isDismissed(DismissDirection.EndToStart)

            if (isDismissed && dismissDirection == DismissDirection.EndToStart
            ) {
                val scope = rememberCoroutineScope()
                scope.launch {
                    delay(300)
                    onSwipeToDelete(Action.DELETE, task)
                }
            }

            AnimatedVisibility(
                visible = !isDismissed,
                exit = shrinkVertically(
                    animationSpec = tween(
                        durationMillis = 300,
                    )
                ),
                enter = expandVertically(
                    animationSpec = tween(
                        durationMillis = 300
                    )
                )
            ) {
                SwipeToDismiss(
                    state = dismissState,
                    directions = setOf(DismissDirection.EndToStart),
                    dismissThresholds = { FractionalThreshold(0.2f) },
                    background = { RedBackground() },
                    dismissContent = {
                        LazyColumnItem(
                            toDoTask = task,
                            navigateToTaskScreen = navigateToTaskScreen
                        )
                    }
                )
            }
        }
    }

}

标签: androidkotlinandroid-jetpack-compose

解决方案


首先,您不应该在可组合内执行任何状态更改操作。取而代之的是使用一种副作用,通常是LaunchedEffect(key) { }:块的内容将在第一次渲染时调用,并且每次键都与最后一次渲染不同。此外,您已经在协程范围内,因此无需启动它。在文档中查看有关副作用的更多信息。

尚不支持列表中的项目动画。它就像添加AnimatedVisibility到项目一样简单。

当 compose 第一次AnimatedVisibility在 compose 树中看到时,它会在没有动画的情况下绘制(或不绘制)它。

并且当下一次重组visible与上次渲染时间不同时,它会进行动画处理。

因此,要使其按您的意愿工作,您可以执行以下操作:

  1. 添加itemAppeared状态值,这将使列表中的项目最初隐藏,并使用副作用使其在渲染后立即可见
  2. 添加columnAppeared这将阻止初始外观动画 - 没有它,当屏幕呈现所有项目时,所有项目也会以动画方式出现
@Composable
fun DisplayTasks(
    tasks: List<ToDoTask>,
    onSwipeToDelete: (Action, ToDoTask) -> Unit,
) {
    var columnAppeared by remember { mutableStateOf(false) }
    LaunchedEffect(Unit) {
        columnAppeared = true
    }
    LazyColumn {
        items(
            items = tasks,
            key = { task ->
                task.id
            }
        ) { task ->
            val dismissState = rememberDismissState()
            val dismissDirection = dismissState.dismissDirection
            val isDismissed = dismissState.isDismissed(DismissDirection.EndToStart)
            if (isDismissed && dismissDirection == DismissDirection.EndToStart
            ) {
                LaunchedEffect(Unit) {
                    delay(300)
                    onSwipeToDelete(Action.DELETE, task)
                }
            }

            var itemAppeared by remember { mutableStateOf(!columnAppeared) }
            LaunchedEffect(Unit) {
                itemAppeared = true
            }
            AnimatedVisibility(
                visible = itemAppeared && !isDismissed,
                exit = shrinkVertically(
                    animationSpec = tween(
                        durationMillis = 300,
                    )
                ),
                enter = expandVertically(
                    animationSpec = tween(
                        durationMillis = 300
                    )
                )
            ) {
                SwipeToDismiss(
                    state = dismissState,
                    directions = setOf(DismissDirection.EndToStart),
                    dismissThresholds = { FractionalThreshold(0.2f) },
                    background = {
                        Box(
                            Modifier
                                .background(Color.Red)
                                .fillMaxSize()
                        )
                    },
                    dismissContent = {
                        Text(task.id)
                    }
                )
            }
        }
    }
}

推荐阅读