【发布时间】:2021-05-08 19:55:21
【问题描述】:
我在惰性列视图中有一个项目列表。
当我们从列表中删除一项时如何显示动画。
我需要为被删除的视图设置动画。删除操作是通过按视图内的删除图标来完成的。
我尝试使用AnimationVisibility,但它没有按预期工作。
【问题讨论】:
标签: android android-jetpack-compose
我在惰性列视图中有一个项目列表。
当我们从列表中删除一项时如何显示动画。
我需要为被删除的视图设置动画。删除操作是通过按视图内的删除图标来完成的。
我尝试使用AnimationVisibility,但它没有按预期工作。
【问题讨论】:
标签: android android-jetpack-compose
Compose 1.1.0 更新:
现在可以动画项目位置更改,但删除/插入动画仍然不可能,需要通过我解释的方法来实现。
要动画项目位置更改,您只需通过key = { it.id } 提供列表中的项目键并使用Modifier.animateItemPlacement()。
原答案
它尚未得到官方支持,但他们正在努力。您可能可以实现它,但以一种 hacky 方式。
当您的列表更新时,您的可组合项会重新创建,并且它还不支持项目的动画,因此您必须在项目上添加一个布尔变量并在它“删除”时更改值,而不是从列表中删除它.显示更新后的列表后,您可以对要移除的项目进行动画处理,然后在动画结束后更新没有它的列表。
我没有亲自测试过这种方法,所以它可能无法按预期工作,但这是我能想到的唯一方法,因为惰性列表不支持更新动画。
【讨论】:
实验性添加了动画延迟列表项位置的功能。
LazyItemScope 中有一个新的修饰符,称为Modifier.animateItemPlacement()。
使用示例:
var list by remember { mutableStateOf(listOf("A", "B", "C")) }
LazyColumn {
item {
Button(onClick = { list = list.shuffled() }) {
Text("Shuffle")
}
}
items(list, key = { it }) {
Text("Item $it", Modifier.animateItemPlacement())
}
}
当您通过LazyListScope.item 或LazyListScope.items 提供键时,此修饰符将启用项目重新排序动画。除了项目重新排序之外,由排列或对齐更改等事件引起的所有其他位置更改也将被动画化。
【讨论】:
就像 YASAN 所说的那样,我能够使用 AnimatedVisibility 在 LazyColumn 项目上生成“slideOut”+“fadeOut”动画,只需在项目的 DataClass 上添加 isVisible 属性并将项目视图包装在 @987654324 中@可组合。由于该 api 仍处于实验阶段,请小心。
供您参考,如果您或其他人可能会寻找它,我将把我的 sn-p 放在这里。
对于LazyColumn
LazyColumn {
items(
items = notes,
key = { item: Note -> item.id }
) { item ->
AnimatedVisibility(
visible = item.isVisible,
exit = fadeOut(
animationSpec = TweenSpec(200, 200, FastOutLinearInEasing)
)
) {
ItemNote(
item
) {
notes = changeNoteVisibility(notes, it)
}
}
}
}
对于可组合项目
@ExperimentalAnimationApi
@ExperimentalMaterialApi
@Composable
fun ItemNote(
note: Note,
onSwipeNote: (Note) -> Unit
) {
val iconSize = (-68).dp
val swipeableState = rememberSwipeableState(0)
val iconPx = with(LocalDensity.current) { iconSize.toPx() }
val anchors = mapOf(0f to 0, iconPx to 1)
val coroutineScope = rememberCoroutineScope()
Box(
modifier = Modifier
.fillMaxWidth()
.height(75.dp)
.swipeable(
state = swipeableState,
anchors = anchors,
thresholds = { _, _ -> FractionalThreshold(0.5f) },
orientation = Orientation.Horizontal
)
.background(Color(0xFFDA5D5D))
) {
Box(
modifier = Modifier
.fillMaxHeight()
.align(Alignment.CenterEnd)
.padding(end = 10.dp)
) {
IconButton(
modifier = Modifier.align(Alignment.Center),
onClick = {
Log.d("Note", "Deleted")
coroutineScope.launch {
onSwipeNote(note)
}
}
) {
Icon(
Icons.Default.Delete,
contentDescription = "Delete this note",
tint = Color.White
)
}
}
AnimatedVisibility(
visible = note.isVisible,
exit = slideOutHorizontally(
targetOffsetX = { -it },
animationSpec = TweenSpec(200, 0, FastOutLinearInEasing)
)
) {
ConstraintLayout(
modifier = Modifier
.offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) }
.fillMaxHeight()
.fillMaxWidth()
.background(Color.White)
) {
val (titleText, contentText, divider) = createRefs()
Text(
modifier = Modifier.constrainAs(titleText) {
top.linkTo(parent.top, margin = 12.dp)
start.linkTo(parent.start, margin = 18.dp)
end.linkTo(parent.end, margin = 18.dp)
width = Dimension.fillToConstraints
},
text = note.title,
fontSize = 16.sp,
fontWeight = FontWeight(500),
textAlign = TextAlign.Start
)
Text(
modifier = Modifier.constrainAs(contentText) {
bottom.linkTo(parent.bottom, margin = 12.dp)
start.linkTo(parent.start, margin = 18.dp)
end.linkTo(parent.end, margin = 18.dp)
width = Dimension.fillToConstraints
},
text = note.content,
textAlign = TextAlign.Start
)
Divider(
modifier = Modifier.constrainAs(divider) {
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
width = Dimension.fillToConstraints
},
thickness = 1.dp,
color = Color.DarkGray
)
}
}
}
}
还有数据类Note
data class Note(
var id: Int,
var title: String = "",
var content: String = "",
var isVisible: Boolean = true
)
【讨论】:
Note 类实现一个DTO 转换器,并将您的isVisible 字段放在那里,而不是放在Note 类本身中。您不希望数据库中有与 UI 相关的字段
如果您使用过 RecyclerView 小部件,您就会知道它会自动为项目更改设置动画。 Lazy 布局尚未提供该功能,这意味着项目更改会导致即时“快照”。您可以关注此错误以跟踪此功能的任何更改。
来源:https://developer.android.com/jetpack/compose/lists#item-animations
【讨论】:
如果您实施compose 版本 1.1.0-beta03。现在,我们有了一种新的方式来为项目设置动画。
【讨论】:
我只是在玩,但也许这样的事情会在他们建立正确的解决方案时有所帮助。
@Composable
fun <T> T.AnimationBox(
enter: EnterTransition = expandVertically() + fadeIn(),
exit: ExitTransition = fadeOut() + shrinkVertically(),
content: @Composable T.() -> Unit
) {
val state = remember {
MutableTransitionState(false).apply {
// Start the animation immediately.
targetState = true
}
}
AnimatedVisibility(
visibleState = state,
enter = enter,
exit = exit
) { content() }
}
LazyColumn(
state = lazyState
) {
item {
AnimationBox {
Item(text = "TEST1!")
}
AnimationBox {
Item(text = "TEST2!")
}
}
}
【讨论】: