Compose 列表
本教程共 100 篇 · 第 45 篇 · 更新于 2026-07-28 · 约 8 分钟阅读
45. Compose 列表
本节目标:学会用 LazyColumn 和 LazyRow 构建高性能列表,掌握 key 标识、items 遍历、网格列表 LazyGrid 的用法,理解懒加载原理。
为什么不用 Column 做列表
Column 会一次性把所有子元素都加载到内存。如果列表有一万条数据,Column 会创建一万个组件—直接卡死。
LazyColumn 只渲染当前屏幕可见的那几项,滚动到新内容时才创建新的,滚出去的回收掉。这和传统 View 体系的 RecyclerView 原理一样,但代码简单得多。
LazyColumn 基础
@Composable
fun SimpleList() {
val items = listOf("苹果", "香蕉", "橙子", "葡萄", "西瓜")
LazyColumn {
items(items) { fruit ->
Text(
text = fruit,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
)
}
}
}
LazyColumn 的内容是一个 LazyListScope lambda,里面用 items() 函数遍历数据。每个数据项对应一个可组合函数。
Note
LazyColumn不需要 Adapter、不需要 ViewHolder、不需要 LayoutManager。一个items()就搞定了,比 RecyclerView 简洁太多。
items 和 itemsIndexed
items() 只给数据,itemsIndexed() 额外给索引:
LazyColumn {
itemsIndexed(items) { index, fruit ->
Text("$index. $fruit")
}
}
key 的重要性
默认情况下,LazyColumn 用列表位置来标识每个 item。但如果你删除、插入、重排数据,位置标识会导致界面错乱—动画乱跳、状态串台。
用 key 参数给每个 item 一个稳定唯一标识:
data class Fruit(val id: Int, val name: String)
LazyColumn {
items(items = fruits, key = { it.id }) { fruit ->
Text(fruit.name)
}
}
Warning
key必须是稳定的唯一值,别用index或会变的值。推荐用数据类的id字段。加了 key 后,列表数据变化时 Compose 能正确追踪每一项,动画和状态都不会乱。
列表项间距和内边距
LazyColumn(
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(fruits) { fruit ->
FruitItem(fruit)
}
}
contentPadding:整个列表的内边距,滚动内容不会被裁切。verticalArrangement.spacedBy():每项之间的间距。
Tip
- 别用
Modifier.padding()包 LazyColumn 外面来做内边距,那样顶部内容会贴边且滚动时被裁。用contentPadding才对。
LazyRow 横向列表
LazyRow 是横向版本,API 和 LazyColumn 一模一样:
LazyRow(
horizontalArrangement = Arrangement.spacedBy(12.dp),
contentPadding = PaddingValues(16.dp)
) {
items(categories) { category ->
Chip(text = category.name)
}
}
网格列表 LazyVerticalGrid
做相册、商品展示这类网格布局用 LazyVerticalGrid:
@Composable
fun PhotoGrid(photos: List<String>) {
LazyVerticalGrid(
columns = GridCells.Fixed(3), // 3 列
contentPadding = PaddingValues(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(photos, key = { it }) { photo ->
AsyncImage(
model = photo,
contentDescription = null,
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
.clip(RoundedCornerShape(8.dp))
)
}
}
}
columns 参数控制列数:
| GridCells | 效果 |
|---|---|
Fixed(3) | 固定 3 列 |
Adaptive(120.dp) | 每列最小 120dp,自动算列数 |
FixedSize(100.dp) | 每列固定 100dp 宽 |
还有横向网格 LazyHorizontalGrid。
混合内容
LazyColumn 不只能放一种 item,可以混合多种类型:
LazyColumn {
// 标题
item {
Text("推荐商品", style = MaterialTheme.typography.titleLarge)
}
// 横向滚动列表
item {
LazyRow {
items(featured) { item -> FeaturedCard(item) }
}
}
// 分隔线
item { HorizontalDivider() }
// 商品列表
items(products, key = { it.id }) { product ->
ProductItem(product)
}
}
item { } 放单个元素,items() 放列表数据。可以随意混合。
stickyHeader 粘性头部
通讯录那种字母索引吸顶效果用 stickyHeader:
LazyColumn {
grouped.forEach { (initial, contacts) ->
stickyHeader {
Text(
initial,
modifier = Modifier
.fillMaxWidth()
.background(Color.LightGray)
.padding(8.dp)
)
}
items(contacts, key = { it.id }) { contact ->
Text(contact.name, modifier = Modifier.padding(16.dp))
}
}
}
Note
stickyHeader是实验性 API,需要加@OptIn(ExperimentalFoundationApi::class)注解。
列表性能要点
- 一定加 key:数据有 id 就用 id 做 key,避免重组混乱。
- 列表项别太重:单个 item 的可组合函数要轻量,复杂逻辑提到外面。
- 别在 items 里做耗时计算:比如在每次重组时都
format日期,应该预先算好。 - 用 immutable 数据:传给列表的数据用不可变集合,Compose 能更好地跳过未变的项。
完整示例:联系人列表
data class Contact(
val id: Int,
val name: String,
val phone: String
)
@Composable
fun ContactList(contacts: List<Contact>) {
LazyColumn(
contentPadding = PaddingValues(vertical = 8.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
items(contacts, key = { it.id }) { contact ->
ContactItem(contact)
}
}
}
@Composable
fun ContactItem(contact: Contact) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.primaryContainer),
contentAlignment = Alignment.Center
) {
Text(contact.name.take(1))
}
Spacer(Modifier.width(12.dp))
Column {
Text(contact.name, fontWeight = FontWeight.Medium)
Text(
contact.phone,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
小结
LazyColumn 和 LazyRow 是 Compose 的列表方案,替代 RecyclerView。核心是 items() 遍历数据、key 稳定标识、contentPadding 和 spacedBy 控制布局。LazyVerticalGrid 做网格。代码量比 RecyclerView 少一个数量级,但性能不输。下一节学习 Compose 的状态管理。