本文内容结合扔物线视频课程、与个人理解、ai案例。
1. 关键入参
- contentPadding:使得 item 可覆盖间距。
- verticalArrangement:通常用 spacedBy,设置子项间间距
- reverseLayout:倒着排版
- LazyListState:控制与监听列表
- 跳转;控制点击 item 时滑动到对应 item。
@Preview
@Composable
private fun LazyRowTest() {
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()
LazyColumn(
...,
state = listState
) {
items(30) { index ->
ListItem(index) {
scope.launch {
listState.scrollToItem(index)
}
}
}
}
}
- 监听第一个可见 item;
除了 firstVisibleItemIndex,还有 firstVisibleItemScrollOffset/layoutInfo 可以获取更多信息。
- userScrollEnabled
控制列表是否可以滑动
- flingBehavior
控制 fling 惯性滑动的物理引擎,默认情况是慢慢减速移动。例如可以控制 fling 速度、停止时将第一个可见的 item 吸附顶部。
- overscrollEffect
滑动过头继续滑动的效果。
2. key(减少重建次数)
首先,构造一个实验场景:
val users = remember {
listOf(
User(1, "张三"),
User(2, "李四"),
User(3, "王五"),
User(4, "赵六"),
User(5, "钱七"),
).toMutableStateList()
}
// 有 key
LazyColumn(...) {
items(users.size, key = { users[it].id } ) { index ->
UserItem(users[index])
}
}
// 无 key,默认 key 为 index
LazyColumn(...) {
items(users.size) { index ->
UserItem(users[index])
}
}
点击「改名」:“张三” 改名为 “张三✓” -> users[0]发生改变 -> 触发两个列表重组。
这是 StateList 快照 的作用。
这时头部插入一个 item,没有 key 的列表项都发生了一次重组。
而有 key 的列表,没有发生新的重组。
这个过程中发生了什么:
- 列表数据变了,LazyColumn 重新遍历所有 item,调用 key lambda 生成新的 key 列表:
旧 key 列表:[1, 2, 3, 4, 5]
新 key 列表:[105, 1, 2, 3, 4, 5]
- 逐个匹配新 key → 旧 key
对新 key 列表中的每一个 key,去旧 key 列表里找:
新 key=105 → 旧列表里找不到 → 标记为「新建」
新 key=1 → 旧列表里找到了 → 标记为「复用」
新 key=2 → 旧列表里找到了 → 标记为「复用」
新 key=3 → 旧列表里找到了 → 标记为「复用」
新 key=4 → 旧列表里找到了 → 标记为「复用」
新 key=5 → 旧列表里找到了 → 标记为「复用」
- 对每个「复用」的 item,执行 composable lambda
这一步才是决定重不重组的关键。以 key=1(张三)为例:
items(users.size, key = { users[it].id }) { index ->
UserItem(users[index]) // ← 执行这个 lambda
}
Compose 执行 lambda,里面会读取 users[index]。这时 Compose 的 State 快照系统介入:
key=1 的 item,现在 index 从 0 变成了 1
读取 users[1] → 得到 User(1, "张三✓")
上次这个 item 读到的也是 User(1, "张三✓")
→ 数据没变 → 不 重组
而对于没有 key 的场景:
旧 key 列表:[0, 1, 2, 3, 4]
新 key 列表:[0, 1, 2, 3, 4, 5]
新 key=0 → 旧列表找到了 → 复用 → 但 users[0] 从"张三✓"变成"新人5" → 重组
新 key=1 → 旧列表找到了 → 复用 → 但 users[1] 从"李四"变成"张三✓" → 重组
新 key=2 → 旧列表找到了 → 复用 → 但 users[2] 从"王五"变成"李四" → 重组
...
新 key=5 → 旧列表找不到 → 新建
3. contentType(减少重建次数)
告诉 LazyColumn:不同 item 的布局类型是什么,以便更高效地复用组合节点。
假设列表有两种布局:文字消息和图片消息,交替排列。
index 0 → TextBubble (一个 Text)
index 1 → ImageBubble (一个 Image + 一个 Text)
index 2 → TextBubble
index 3 → ImageBubble
...
- 不填 contentType:
向下滚动,index 0 滑出,index 4 (TextBubble) 要滑入:
运气好:从池里拿到 TextBubble 节点 → 结构匹配 → 只更新数据 → 快
继续滚动,index 1 滑出,index 5 (ImageBubble) 要滑入:
运气差:池里先进先出,拿到的是 TextBubble 节点 → 结构不匹配 → 整个 composition 要拆掉重建 → 慢
- 填 contentType
回收池按类型分开,永远不会拿错类型。