在 Jetpack Compose 中,Modifier.animateItem() 是从 Compose 1.7.0 开始引入的一个强大且常用的修饰符(它替代了早期被废弃的 animateItemPlacement)。
一、 animateItem 的核心作用
Modifier.animateItem() 专门用于 LazyColumn 或 LazyRow 中,为列表项的动态变化提供开箱即用的平滑过渡动画。它能够同时处理以下三种场景:
- 位置重排(Reordering /
Placement):当列表项因为插入、删除或排序改变位置时,它们会平滑地滑动到新位置,而不是瞬间"闪现"。 - 出现/插入(Appearance / Fade In):当有新项被插入到列表中时,它会以淡入(Fade In)等动画形式出现。
- 消失/删除(Disappearance / Fade Out):当项被从列表中移除时,它会以淡出动画形式消失。
二、 使用的核心前提
必须提供稳定的 key:在 LazyColumn 的 items() 中,必须通过 key 参数为每个 item 指定一个唯一且稳定的标识(如数据的 ID)。如果使用默认的索引(index)或不传 key,Compose 将无法在数据发生变动时正确追踪身份,从而导致动画失效或混乱。
三、 完整代码例子
下面的例子展示了一个包含添加、删除和随机打乱(重排)功能的列表,完美应用了 Modifier.animateItem()
data class Task(val id: Int, val title: String)
@Composable
fun AnimatedItemListExample() {
// 模拟列表数据源
var tasks by remember {
mutableStateOf(
listOf(
Task(1, "学习 Jetpack Compose"),
Task(2, "了解 animateItem 作用"),
Task(3, "编写示例代码")
)
)
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
// 操作按钮区域
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Button(onClick = {
val newId = (tasks.maxOfOrNull { it.id } ?: 0) + 1
// 在顶部插入新项
tasks = listOf(Task(newId, "新任务 $newId")) + tasks
}) {
Text("添加")
}
Button(onClick = {
// 随机打乱列表顺序(测试重排动画)
tasks = tasks.shuffled()
}) {
Text("打乱排序")
}
}
// 列表区域
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
// 【关键点】:必须提供 key,否则动画无法正确追踪目标
items(
items = tasks,
key = { task -> task.id }
) { task ->
Card(
modifier = Modifier
.fillMaxWidth()
// 【关键点】:在最外层 Modifier 中链式调用 animateItem()
.animateItem(
// 可选:自定义动画效果(不传则使用默认的优雅弹簧动画)
fadeInSpec = tween(durationMillis = 300),
fadeOutSpec = tween(durationMillis = 300),
placementSpec = spring(stiffness = 300f)
)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(text = task.title, style = MaterialTheme.typography.bodyLarge)
IconButton(onClick = {
// 删除当前项(测试删除渐隐动画)
tasks = tasks.filter { it.id != task.id }
}) {
Icon(Icons.Default.Delete, contentDescription = "删除")
}
}
}
}
}
}
}
四、 自定义动画效果
animateItem() 允许你传参来精确控制各个动画的贝塞尔曲线或物理弹簧效果:
- fadeInSpec: 控制元素出现时的渐入动画(如 tween(200))。
- fadeOutSpec: 控制元素消失时的渐出动画(如 tween(200))。
- placementSpec: 控制元素位置移动时的滑动动画(如默认的 spring() 弹簧效果或 tween())。