源码仓库 :ComposeDemo(分支
main)
技术目标
- 理解 懒组合 :可视区域附近的 item 才会进入组合,滚出可视会 dispose (
remember随之清空,除非用rememberSaveable等策略)。 - 正确使用
items()/itemsIndexed()与key,避免滑动时 状态串行。 - 知道
itemlambda 的捕获范围 与 参数稳定性(与 07 篇联动)会影响重组范围与跳过行为。
1. DSL 结构
kotlin
LazyColumn(
modifier = ...,
state = rememberLazyListState(),
) {
item { /* 单格:头图、间距块、footer */ }
items(list, key = { it.id }, contentType = { it.type }) { row -> /* 每行 */ }
// stickyHeader { ... } 等 API 以当前 Material/Compose 版本文档为准
}
item { }:不参与items的key差分,适合静态头尾。items/itemsIndexed:列表差分、动画、状态恢复都依赖 稳定的key。
2. key 的技术原因
key 给 Compose item 身份 :同一 key 的 item 在列表变化时被视为「同一行」的延续,从而:
- 保留
remember状态(如展开、输入中文字)。 animateItemPlacement()等位移动画不会「飞错行」。
常见错误:
| 做法 | 后果 |
|---|---|
| 错误 key(如随机 uuid 每次变) | 每帧重建 item,性能差且状态丢失 |
仅用 index 作 key 且列表 中间插入/删除 |
插入点之后所有行的 身份整体漂移 → checkbox 勾选跑到别的行 |
| 不同业务实体共用 key | 数据覆盖、动画错乱 |
本仓库 LazyListSampleScreen.kt 使用 DemoRow(id = index, ...) 且 key = { it.id } :在 静态、只追加式 的 demo 数据下等价于 index;真实业务 应使用服务端主键、稳定 uuid 等,而不是「当前列表下标」。
3. Modifier.animateItemPlacement()
启用 item 位移动画时,错误 key 常表现为「飞错行」。排障顺序建议:
- 关闭动画,确认数据与 key 是否正确。
- 打开动画,观察是否仅 插入/删除 行在动。
4. 性能相关(进阶入口)
contentType:不同类型 item(头图 / 文本 / 视频)帮助复用池与测量缓存策略;大列表值得加。- 避免在 item lambda 里捕获整个
ViewModel:把展示所需字段拆成 稳定、细粒度 参数传入子 Composable(见 07 篇)。 - 图片 :大列表解码用 Coil / Glide Compose 等,约束尺寸,避免按原图 decode 撑爆内存。
verticalArrangement = spacedBy:本仓库用于 item 间距;注意与 item 内部padding的叠加是否符合设计。
5. 仓库路径与代码锚点

rows:remember { List(40) { ... } },避免每次重组新建列表引用。items(items = rows, key = { it.id }) { row -> ... }:标准写法模板。
6. 与 Modifier.verticalScroll 的差异(排障)
| 场景 | 更适用 |
|---|---|
| 少量固定子项、一次全部组合可接受 | Column + verticalScroll |
| 大量行、需要视窗懒加载 | LazyColumn |
同向嵌套(Lazy 里再 Lazy、LazyColumn + verticalScroll)易手势冲突,见 02 篇 nestedScroll 提示。
7. 风险清单
- 嵌套纵向
LazyColumn:需 明确高度约束 或nestedScroll连接。 rememberSaveable保存LazyListState与 分页游标 两套真相:恢复滚动位置与重新拉页逻辑要统一,避免「列表顶部但游标在第二页」。- 分页追加 时若整表
key策略变化,可能触发大范围重建;追加页应保持稳定 key 空间。
8. 自检清单
- 列表每一项的
key是否在插入/删除/重排后仍指向同一业务实体? - item 内是否有 大对象捕获 或 非 remember 的昂贵对象创建?
- 头尾是否误用
items导致 多余 key 与差分 ?静态块用item { }。 - 图片与异步内容是否有 尺寸约束与取消(滚出视窗不再加载)?