首页 / Android 入门教程 / Compose 列表

Android 入门教程

Compose 列表

本教程共 100 篇 · 第 45 篇 · 更新于 2026-07-28 · 约 8 分钟阅读

AndroidAndroid 入门教程Jetpack ComposeLazyColumnLazyRow列表LazyGrid

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) 注解。

列表性能要点

  1. 一定加 key:数据有 id 就用 id 做 key,避免重组混乱。
  2. 列表项别太重:单个 item 的可组合函数要轻量,复杂逻辑提到外面。
  3. 别在 items 里做耗时计算:比如在每次重组时都 format 日期,应该预先算好。
  4. 用 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 稳定标识、contentPaddingspacedBy 控制布局。LazyVerticalGrid 做网格。代码量比 RecyclerView 少一个数量级,但性能不输。下一节学习 Compose 的状态管理。