我尝试使用 AnimatedVisibility 和 Modifier.pointerInput 函数来实现用户可以将其滑出以删除列表中的项目的效果。我发现 exit 和 Enter animate 显示不正确,这让我很困惑。项目出现和消失没有任何动画。我不知道出了什么问题。我想知道为什么会这样,或者是否有任何方法可以帮助我确定为什么预期的动画不起作用。预先感谢。
@Composable
fun SwipedContent (
swipeThreshold: Float = 500f,
isVisibility: () -> Boolean,
modifier: Modifier = Modifier,
onSwipe: () -> Unit,
content: @Composable () -> Unit
) {
val offsetX = remember { Animatable(0f) }
val scope = rememberCoroutineScope()
AnimatedVisibility (
visible = isVisibility.invoke(),
enter = fadeIn(tween(300)) + slideInHorizontally(
initialOffsetX = { it }
),
exit = fadeOut(tween(300)) + slideOutHorizontally(
targetOffsetX = { fullWidth ->
if (fullWidth > 0) {
fullWidth
} else {
-fullWidth
}
}
)
) {
Box (
modifier = modifier
.pointerInput(Unit) {
detectHorizontalDragGestures(
onDragEnd = {
if (offsetX.value > swipeThreshold || offsetX.value < -swipeThreshold) {
onSwipe.invoke()
} else {
scope.launch { offsetX.animateTo(0f) }
}
}
) { change, dragAmount ->
change.consume()
scope.launch { offsetX.snapTo(offsetX.value + dragAmount) }
}
}
.offset { IntOffset(offsetX.value.roundToInt(), 0) },
) {
content.invoke()
}
}
}
initialAlertList.forEachIndexed { index, alert ->
key (alert.id) {
SwipedContent (
isVisibility = { initialAlertList.contains(alert) },
onSwipe = { initialAlertList.remove(alert) }
) {
Card (
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp, horizontal = 5.dp)
.clickable(
onClick = {
editAlertIndexState.intValue = index
isShowPopUp.value = !isShowPopUp.value
},
indication = ripple(),
interactionSource = remember { MutableInteractionSource() }
),
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors().copy(containerColor = Color.Transparent),
border = BorderStroke(width = 1.dp, color = Color.Gray),
) {
// someComponents here
}
}
}
}
在
onSwipe
回调中,您从列表中删除该项目。这会触发重新组合,并再次执行 initialAlertList.forEachIndexed
循环,但相关项目不再存在,因此该项目的 SwipedContent
被跳过,以及其中的 AnimatedVisibility,因此无法显示不再退出动画了。
您需要在滑动时将项目保留在列表中,并将其标记为已删除。一种方法是将新属性添加到
Alert
数据类:
val visible: Boolean = true,
然后你可以像这样调用 SwipedContent :
SwipedContent(
isVisibility = { alert.visible },
onSwipe = { initialAlertList[index] = alert.copy(visible = false) },
)
当用户滑动时,该项目被标记为
visible = false
但仍保留在列表中。重组时,仍会为该“已删除”项目调用 SwipedContent(它并未真正删除,只是标记为不可见),并且其 AnimatedVisibility 将识别可见性的变化并触发退出动画。
如果您想等待动画结束,然后真正从列表中删除该项目,请参阅此处:https://stackoverflow.com/a/68640459
我猜您已将
initialAlertList.forEachIndexed
循环与 key
放在列中。这似乎有点尴尬;你为什么不直接使用一个内置的简单的 LazyColumn 呢?
LazyColumn {
itemsIndexed(
items = initialAlertList,
key = { _, item -> item.id },
) { index, alert ->
SwipedContent(/*...*/)
}
}
当项目不适合时,这还可以启用列滚动,您甚至可以将
Modifier.animateItem()
传递给 SwipedContent。这将允许 LazyColumn 使项目跟随被删除的项目轻轻地向上滑动以填充其空间,而不是立即跳起来。仅当该项目实际上从列表中“删除”时才有效,而不仅仅是设置为不可见。请参阅上一节的最后一段。
最后,通过您的 SwipedContent,您似乎重新发明了内置的 。为此,您需要一个 rememberSwipeToDismissBoxState()
,您可以在其中设置
500f
阈值,但它也不提供自动触发的退出动画,因此您必须在调用状态的 confirmValueChange
时创建自己的动画与所需的值。仅供参考。