Compose Foundation
Compose 底层 · androidx.compose.foundation
Lazy 列表1.13.0-alpha01
用稳定 key、contentType、LazyListState 和分页结构建立可维护的长列表。
最后更新 2026-08-01
最小结构
val listState = rememberLazyListState()
LazyColumn(state = listState) {
items(
items = messages,
key = { it.id },
contentType = { it.kind },
) { message ->
MessageRow(message)
}
}Lazy 容器根据视口按需组合和布局,适合数量多或规模不定的集合。
items 变体
| 函数 | 用途 |
|---|---|
item { } | 单个头部/尾部 |
items(list, key, contentType) | 普通列表 |
itemsIndexed | 需要 index 时 |
items(count) | 按数量(如骨架屏) |
stickyHeader { } | 粘性头部(LazyColumn 专属) |
LazyColumn {
item(key = "header") { Header() }
items(items = data, key = { it.id }, contentType = { "row" }) { Row(it) }
item(key = "footer") { Footer() }
}key 保护状态身份
列表头部插入项目后,没有稳定 key 时原位置的 remember 状态跟随位置而不是业务项:
// ❌ 位置身份:头部插入一条后,后续项状态错位
items(messages) { MessageRow(it) }
// ✅ 业务身份:id 不变状态就不变
items(messages, key = { it.id }) { MessageRow(it) }key 要求:唯一、稳定,类型满足状态保存要求(可序列化/可比较)。
contentType 帮助复用
混合标题、图片卡片、普通行时:
LazyColumn {
items(
items = feed,
contentType = { item ->
when (item) {
is FeedItem.Header -> "header"
is FeedItem.Card -> "card"
else -> "row"
}
},
) { ... }
}contentType告诉实现哪些 item 结构可互相复用(减少重组/重绘)。- 只有多种布局类型时才需要;不要为每个 item 生成唯一类型(会破坏复用)。
读取滚动状态
// UI 判断:derivedStateOf,滚动时才重算
val showTop by remember {
derivedStateOf { listState.firstVisibleItemIndex > 0 }
}
// 埋点/触发:snapshotFlow 流式处理
LaunchedEffect(listState) {
snapshotFlow { listState.firstVisibleItemIndex }
.distinctUntilChanged()
.collect { index -> analytics.onScroll(index) }
}不要在组合中高频读 layoutInfo——每次滚动都会让读取范围重组。
常见陷阱
- 无 key:插入/重排后状态错位。
- contentType 每项唯一:破坏复用,反而更慢。
- 组合中读 layoutInfo:滚动全程重组。
- Lazy 里嵌无界高度 Column:
LazyColumn { item { Column(Modifier.fillMaxHeight()) } }可能崩或行为怪异——用固定高度或heightIn(max)。 - 乱用 scrollToItem 大 index:跳很远用
scrollToItem,就近用animateScrollToItem。
复制即用
@Composable
fun MessageList(messages: List<Message>) {
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()
LazyColumn(
state = listState,
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
item(key = "header") { ListHeader() }
items(
items = messages,
key = { it.id },
contentType = { "message" },
) { message ->
MessageRow(message)
}
}
}要点
- Lazy 容器按需组合;items 变体覆盖头部/列表/粘性头。
- key 用稳定业务 id 保护状态身份。
- contentType 区分布局类型帮助复用;别每项唯一。
- 滚动状态用 derivedStateOf/snapshotFlow,别高频读 layoutInfo。