☰
Jetpack Compose列表完全指南:LazyColumn核心原理与实战
2026/10/19 6:53:01 网站建设 项目流程

自接触 Jetpack Compose 这几个月以来,我最大的感受是:列表在 Compose 里的写法和传统 View 体系完全不是一个路子。你习惯了 RecyclerView + Adapter 后,第一次打开 LazyColumn 可能会觉得“就这么点代码?”,但真要处理动态列表、点击刷新、多类型 item、状态丢失这些场景,又会发现有很多隐藏的知识点。

这篇教程是我整理的“Jetpack Compose 安卓声明式 UI 开发”系列第 8 篇,专门讲列表。我把整个内容拆成:核心概念 → 环境准备 → 基础组件 → 全流程实战 → 进阶技巧 → 常见问题 → 工程建议,从头捋一遍。无论你是刚接触 Compose 的新手,还是想系统补一补声明式列表知识的开发者,这篇都可以直接作为学习资料收藏。

1. 为什么“列表”在 Compose 里值得单独讲

1.1 声明式 UI 与传统 View 的本质区别

先想想以前我们怎么做列表。

在传统View体系中,开发一个列表通常需要三步:

  1. 在 XML 里写一个RecyclerView。
  2. 新建一个Adapter类,实现onCreateViewHolder、onBindViewHolder、getItemCount。
  3. 把数据列表交给 Adapter,再通过notifyDataSetChanged()或DiffUtil通知界面刷新。

这套模式叫命令式 UI。核心逻辑是:你手动控制“View 什么时候创建”“数据什么时候绑定”“变化怎么通知界面”。

而 Jetpack Compose 是声明式 UI。声明式的意思是:你只需要描述“当前状态应该呈现成什么样子”,剩下的创建、更新、销毁交给框架。

列表也不例外。

LazyColumn { items(articleList) { article -> ArticleItem(article) } }

这段代码表达的含义是:把 articleList 逐条渲染成 ArticleItem。如果 articleList 变了,LazyColumn 内部会自动对比、更新、复用可组合项,不需要你写 Adapter,也不需要手动调用刷新方法。

这就是声明式和命令式最核心的区别:你不再告诉界面“怎么变”,而是告诉它“现在是什么”。

1.2 为什么列表在 Compose 里是一个独立主题

可能你会问:列表不就是把几个 Text 排成一列吗?为什么值得单独开一篇?

因为列表在移动应用里承担的是“大量数据 + 滑动浏览 + 动态刷新”的职责,它对性能、内存、状态保持的要求远高于普通静态布局。Compose 作为声明式框架,它解决列表问题的方式是 LazyColumn / LazyRow,但“懒加载”这个机制背后有很多容易被忽略的细节:

  • 列表项什么时候被创建?什么时候被销毁?
  • 滚动出屏幕后,item 状态还在不在?
  • 数据变了,怎么精准更新某一条而不是全量重绘?
  • 多类型 item 怎么声明?
  • 加载更多、分页、空状态怎么优雅处理?

这些问题都不是“写一行 LazyColumn”能直接回答的,所以我把列表单独拎出来做一集,并且尽量把核心用法和工程经验都覆盖到。

本集内容是系列教程中的第 8 篇,假设你已经掌握了 Compose 的基础环境搭建、@Composable函数、常见的布局组件(Column、Row、Box)和状态管理的基本用法。如果你刚入门,建议先熟悉前面的基础内容再回来看这一篇。

2. 环境准备:把 Compose 项目跑起来

2.1 开发环境与版本说明

写 Compose 列表之前,我们先把环境跑通。下面以常见的 Android Studio 开发环境为例。

要注意:具体版本号需要根据你的项目实际情况调整。Compose 从发布到现在迭代很快,如果你是从零搭建项目,建议直接在 Android Studio 新建工程时选择 “Empty Activity”,并且在模板里勾选 Compose 支持;如果你是给已有项目加 Compose,则要确认 Gradle、Kotlin、Compose 编译器之间的版本匹配。

简单来说,Compose 项目通常需要满足:

  • Android Studio 版本足够新,能正常创建 Compose 模板项目。
  • Kotlin 版本与 Compose 编译器插件版本兼容。
  • 使用 Compose BOM(Bill of Materials)统一管理 Compose 各组件版本,而不是手动一个个写版本号。

2.2 添加 Compose 依赖

假设你是在已有 Android 项目里接入 Compose,核心依赖大致如下。这里以 Kotlin DSL(build.gradle.kts)为例:

// 文件路径:app/build.gradle.kts android { buildFeatures { compose = true } composeOptions { // kotlinCompilerExtensionVersion 需要和你项目的 Kotlin 版本匹配 // 示例写法,实际版本请按官方文档调整 kotlinCompilerExtensionVersion = "1.5.10" } } dependencies { val composeBom = platform("androidx.compose:compose-bom:2024.06.00") implementation(composeBom) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.material3:material3") implementation("androidx.compose.foundation:foundation") // 可选:生命周期相关组件 implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.3") implementation("androidx.activity:activity-compose:1.9.0") debugImplementation("androidx.compose.ui:ui-tooling") }

如果是 Groovy DSL(build.gradle),写法类似:

dependencies { implementation platform('androidx.compose:compose-bom:2024.06.00') implementation 'androidx.compose.ui:ui' implementation 'androidx.compose.material3:material3' }

这里用 BOM 的好处是:你只需要指定一次 BOM 版本,下面几个androidx.compose.*库的版本都会由 BOM 统一对齐,避免冲突。

为什么需要 Foundation 库?
LazyColumn、LazyRow 都是androidx.compose.foundation.lazy包下的组件,所以必须引入foundation依赖。

2.3 常见构建报错处理

刚搭环境时最容易遇到的是版本不匹配:

  • “Compose compiler plugin 需要 Kotlin 版本 X”:说明composeOptions.kotlinCompilerExtensionVersion与 Kotlin 版本不对应,按错误提示调整。
  • “Unable to find annotation processor”:通常和 Kotlin 或插件配置缺失有关,检查是否启用了kotlin("plugin.compose")。
  • 混淆模式下列表点击失效:一般和 release 混淆规则有关,需要保留 Compose 相关类,但这属于后期问题。

环境跑通之后,我们就可以开始正题了。

3. Compose 列表核心组件:LazyColumn 与 LazyRow

3.1 LazyColumn 与 LazyRow 是什么

LazyColumn是 Compose 里用于“垂直方向”懒加载列表的组件,对应传统 View 体系中的RecyclerView。

它有几个关键特点:

  1. 懒加载:只组合那些需要显示在屏幕上的 item,以及少量预加载的缓冲 item。列表很长时,不会一次性创建所有 item。
  2. 自动回收复用:item 滑出屏幕后,Compose 会收回它的组合资源,滑回来时重新创建。
  3. 声明式 API:在LazyColumn的content作用域里声明 item 的组成,而非通过 adapter 绑定。

LazyRow则是横向版本的懒加载列表,适合做横滑卡片、图片轮播等场景。

基础用法:

@Composable fun SimpleListScreen() { val items = listOf("Android", "Kotlin", "Compose", "LazyColumn") LazyColumn( modifier = Modifier.fillMaxSize() ) { items(items) { name -> Text( text = name, modifier = Modifier .fillMaxWidth() .padding(16.dp), fontSize = 18.sp ) } } }

看到没有:没有 Adapter,没有 ViewHolder,没有getItemCount()。只需要一个items()作用域函数,把一个数据列表传进去,列表项怎么展示由一个普通 Composable 描述。

3.2 item、items、itemsIndexed 的区别

很多初学者会在LazyColumn的 content 作用域里被三个函数搞混。

1.item:添加单个列表项

LazyColumn { item { Text("这是一个单独的表头") } item { Text("这是另一个单独项") } }

适合固定数量、内容独立的列表项,比如列表头部的 Banner、中间的广告位、底部“加载中”提示。

2.items:传入一个列表,逐条渲染

LazyColumn { items(articleList) { article -> ArticleItem(article) } }

这里的items是一个扩展函数,它接收一个List<T>,并为每个元素调用一次后面的渲染 lambda。这是最常用的写法。

3.itemsIndexed:渲染时同时拿到下标

LazyColumn { itemsIndexed(articleList) { index, article -> Text("第 $index 条:${article.title}") } }

适合需要显示序号、间隔线、奇偶行背景等场景。

items还有一个重载,可以接收带 key 的数据结构,比如:

LazyColumn { items( items = articleList, key = { it.id } ) { article -> ArticleItem(article) } }

这个key参数非常关键,我们下一节细说。

3.3 key 参数:为什么列表项必须有稳定身份

在 Compose 中,LazyColumn 会通过 item 在列表中的位置来识别它。但如果你的列表是动态的,比如用户删除了一条、或者拉到新数据后插入了新条目,那么位置会变化,Compose 就需要一种方式判断“哪个 item 还是原来的那个”。

key就是干这个用的。

LazyColumn { items( items = articleList, key = { article -> article.id } ) { article -> ArticleItem(article) } }

当key稳定且唯一时,Compose 能保持 item 的状态——比如列表项内的输入框内容、滚动位置、开关状态。如果不加key,当列表在头部插入数据时,Compose 可能把旧的 item 状态错配到新的数据上。

实际项目里,列表数据一定要有稳定唯一的 id,最好用数据库主键或服务端返回的 id,不要再随手用 index。

关于 index 的一个更隐蔽的问题:如果 list 是mutableListOf并且在组合过程中被修改,会导致运行时崩溃或者项乱跳。后面常见问题里会提到。

4. 完整实战:做一个文章列表 App

下面我们从头写一个文章列表界面,把上面讲到的内容落到代码里。为了便于理解,我们拆成几步。

4.1 设计数据结构

先定义新闻/文章的数据模型:

// 文件路径:app/src/main/java/com/example/listdemo/model/Article.kt package com.example.listdemo.model data class Article( val id: Int, val title: String, val summary: String, val author: String, val readCount: Int )

这里我们使用data class,因为 Compose 列表经常需要比较数据内容,data class会自动实现equals()和hashCode(),方便我们后续做状态判断。

再写一个模拟数据源,方便测试:

// 文件路径:app/src/main/java/com/example/listdemo/data/MockData.kt package com.example.listdemo.data import com.example.listdemo.model.Article object MockData { fun getArticles(): List<Article> { return List(50) { index -> Article( id = index + 1, title = "Jetpack Compose 系列教程第 ${index + 1} 篇", summary = "这是第 ${index + 1} 篇文章的摘要,用来展示 Compose 列表的懒加载效果。", author = if (index % 2 == 0) "张三" else "李四", readCount = (1000 + index * 35) ) } } }

这里用List(50)快速生成 50 条数据,用来验证 LazyColumn 的懒加载性能。

4.2 定义列表项 UI

列表项的 UI 我们用Card包一层,方便观察视觉效果。

// 文件路径:app/src/main/java/com/example/listdemo/ui/ArticleItem.kt package com.example.listdemo.ui import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Card import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.example.listdemo.model.Article @Composable fun ArticleItem( article: Article, onClick: (Article) -> Unit ) { Card( onClick = { onClick(article) }, modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 8.dp), shape = RoundedCornerShape(12.dp) ) { Column( modifier = Modifier.padding(16.dp) ) { Text( text = article.title, style = MaterialTheme.typography.titleMedium ) Text( text = article.summary, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.padding(top = 6.dp) ) Row( modifier = Modifier .fillMaxWidth() .padding(top = 10.dp), horizontalArrangement = Arrangement.SpaceBetween ) { Text( text = "作者:${article.author}", style = MaterialTheme.typography.labelMedium ) Text( text = "阅读 ${article.readCount}", style = MaterialTheme.typography.labelMedium ) } } } }

在 Compose Material3 中,Card可以直接传入onClick参数,变成一个可点击组件,不需要额外包一层Modifier.clickable。这样代码更简洁,也保留了 Card 本身的阴影和圆角效果。

4.3 组装 LazyColumn

现在把数据源和列表项组合进一个页面。

// 文件路径:app/src/main/java/com/example/listdemo/ui/ArticleListScreen.kt package com.example.listdemo.ui import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.material3.TopAppBar import com.example.listdemo.data.MockData import com.example.listdemo.model.Article @OptIn(ExperimentalMaterial3Api::class) @Composable fun ArticleListScreen( onArticleClick: (Article) -> Unit ) { val articles = MockData.getArticles() Scaffold( topBar = { TopAppBar( title = { Text("Compose 列表实战") } ) } ) { innerPadding -> LazyColumn( modifier = Modifier .fillMaxSize() .padding(innerPadding), contentPadding = PaddingValues(vertical = 8.dp) ) { items( items = articles, key = { article -> article.id } ) { article -> ArticleItem( article = article, onClick = onArticleClick ) } } } }

到这里,一个最基本的列表页面就完成了。

contentPadding 的作用:它给 LazyColumn 整体内容区域加内边距,但不会影响列表的滚动范围。换句话说,列表滑到最底部时仍能看到底部留白,不会贴着屏幕边缘。

4.4 点击交互与状态更新

实际项目中,列表往往不是静态的。比如点击收藏按钮要更新状态、点击 item 要跳转详情。

我们给 ArticleItem 增加一个“收藏”状态:

@Composable fun ArticleItem( article: Article, isFavorite: Boolean, onClick: (Article) -> Unit, onToggleFavorite: (Article) -> Unit ) { Card( onClick = { onClick(article) }, modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 8.dp) ) { Column(modifier = Modifier.padding(16.dp)) { Text(text = article.title, style = MaterialTheme.typography.titleMedium) Text(text = article.summary, style = MaterialTheme.typography.bodyMedium) Row( modifier = Modifier .fillMaxWidth() .padding(top = 10.dp), horizontalArrangement = Arrangement.SpaceBetween ) { Text(text = "作者:${article.author}", style = MaterialTheme.typography.labelMedium) Text( text = if (isFavorite) "★ 已收藏" else "☆ 收藏", modifier = Modifier.clickable { onToggleFavorite(article) }, color = if (isFavorite) Color(0xFFFF9800) else Color.Gray ) } } } }

在列表层管理状态:

@Composable fun ArticleListScreen() { val articles = remember { MockData.getArticles() } val favoriteIds = remember { mutableStateOf(setOf<Int>()) } LazyColumn { items( items = articles, key = { it.id } ) { article -> ArticleItem( article = article, isFavorite = article.id in favoriteIds.value, onClick = { /* 跳转详情 */ }, onToggleFavorite = { target -> favoriteIds.value = if (target.id in favoriteIds.value) { favoriteIds.value - target.id } else { favoriteIds.value + target.id } } ) } } }

这里维护了一个favoriteIds集合。点击收藏时,通过不可变集合的+/-操作生成新集合,触发重组。只有那条 item 的isFavorite发生变化,Compose 会自动重组显示文字。

注意:不要直接修改favoriteIds.value内部内容,比如add(),因为 Compose 只有在状态对象被赋新值时才认为状态发生了变化。

4.5 横滑列表 LazyRow 示例

除了竖向列表,横向列表也非常常用。比如首页顶部的“热门标签”或“推荐话题”。

下面是一个简单的 LazyRow 示例:

@Composable fun HotTagRow(tags: List<String>) { LazyRow( contentPadding = PaddingValues(horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { items(tags) { tag -> Surface( shape = RoundedCornerShape(50), color = MaterialTheme.colorScheme.secondaryContainer, modifier = Modifier.padding(vertical = 4.dp) ) { Text( text = tag, modifier = Modifier.padding(horizontal = 14.dp, vertical = 6.dp), style = MaterialTheme.typography.labelMedium ) } } } }

horizontalArrangement = Arrangement.spacedBy(8.dp)可以让每个横向 item 之间自动加 8dp 间距,比在每个 item 上单独加 padding 更优雅。

5. Compose 列表进阶技巧

5.1 不同 item 类型混合

一个真实 App 的列表往往不是单一类型,而是头部 Banner、普通文章、推荐卡片、底部加载状态混在一起的。Compose 里有两种常见做法。

做法一:在 LazyColumn 中手动组合不同类型

LazyColumn { item { Banner() } items(articleList, key = { it.id }) { article -> ArticleItem(article) } item { LoadingFooter() } }

这种方式适合类型不多、顺序相对固定的场景。

做法二:用 sealed class 抽象统一数据源

如果列表数据来自多个接口、顺序动态变化,我倾向于定义统一的数据模型,再在渲染时做分支。

sealed interface ListItem { data class ArticleItemData(val article: Article) : ListItem data class BannerItemData(val imageUrl: String) : ListItem data class AdItemData(val adText: String) : ListItem }

然后在 LazyColumn 中通过 when 分支:

LazyColumn { items(items = mixedList, key = { it.hashCode() }) { item -> when (item) { is ListItem.ArticleItemData -> ArticleItem(item.article) is ListItem.BannerItemData -> Banner() is ListItem.AdItemData -> Text(item.adText) } } }

注意:使用hashCode()作为 key 不够稳定,生产环境还是建议给每个 item 一个唯一 id 字段,尤其在列表会动态变化的情况下。

5.2 加载更多与分页思路

LazyColumn 提供了一个非常在意的监听时机:rememberLazyListState()可以拿到列表滚动状态,配合derivedStateOf判断是否需要加载更多。

val listState = rememberLazyListState() val shouldLoadMore by remember { derivedStateOf { val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull() lastVisibleItem?.index != null && lastVisibleItem.index >= listState.layoutInfo.totalItemsCount - 3 } } LaunchedEffect(shouldLoadMore) { if (shouldLoadMore) { viewModel.loadMore() } } LazyColumn( state = listState ) { items(articles) { article -> ArticleItem(article) } }

这段代码的思路:当最后可见 item 的 index 接近列表总数时,触发加载更多。derivedStateOf可以避免在每次滚动时都触发不必要的重组。

5.3 空状态与加载状态

列表数据为空时,直接展示一个空白页面体验很差。我们可以用if/else控制三种状态:

when { isLoading -> LoadingView() isEmpty -> EmptyView() else -> LazyColumn { items(articles) { ... } } }

LoadingView显示 CircularProgressIndicator,EmptyView显示一张占位图和“暂无数据”文案。这样结构清晰,也方便测试。

5.4 列表性能观察点

Compose 列表的性能问题,多数不是 LazyColumn 本身慢,而是单个 item 内部太复杂。重点关注:

  1. item 里是否做了耗时操作?
  2. item 里是否使用了过大的图片?
  3. 每个 item 是否存在不必要的重组?

关于第三点,可以用Layout Inspector或Compose 编译器插桩观察重组次数。如果单个 item 频繁重组,可以检查状态读取范围是否过大,以及是否缺少稳定 key。

6. 常见问题与排查思路

6.1 问题排查表格

问题现象常见原因解决思路
LazyColumn 滚动后数据错乱列表项 key 缺失或重复为 items 提供基于 id 的稳定 key
列表顶部插入数据后 item 状态错乱没有使用稳定 key使用唯一 id 作为 key
修改列表数据后界面不刷新直接修改了可变集合内部内容创建新集合赋值给 mutableStateOf
页面滑动卡顿item 内部布局复杂、图片过大简化 item UI、使用图片懒加载库
运行时 ConcurrentModificationException组合期间修改了 MutableList使用不可变快照或在非组合阶段修改
列表点击无响应Card 的 onClick 没写或者被子组件拦截检查点击事件绑定、子组件可点击属性
底部间距被遮挡没有设置 contentPadding给 LazyColumn 设置 contentPadding 或 bottomBar 边距

6.2 三个高频坑的详细说明

坑 1:动态列表没有 key 导致状态错乱

这是用 LazyColumn 最容易踩的坑。来看一个错误示例:

// 不要这样做 LazyColumn { itemsIndexed(articles) { index, article -> ArticleItem( article = article, isFavorite = favoriteIds.contains(article.id) ) } }

不使用 key 时,Compose 按 index 映射列表项。如果用户在列表头部删了一条,后面的每一项都会往前挪一位,原本的收藏状态可能迁移到错误的数据上。

解决方式:给items指定 key。

坑 2:Compose 列表项内部状态丢失

比如 item 内有一个输入框,滑出屏幕再滑回来,输入内容不见了。原因通常是 item 被回收后组合状态没有保存。

解决方式:

  • 使用rememberSaveable保存状态。
  • 把输入内容提升到 ViewModel 或父级状态中,而不是只存在 item 内部。
  • 确保 key 稳定,使 Compose 能保存 item 的状态。

坑 3:Compose 编译器版本不匹配导致构建失败

这个在环境准备那节提到过。Compsoe 编译器插件并不是独立版本,它和 Kotlin 版本有一一对应关系。如果升级 Kotlin,忘记升级composeOptions.kotlinCompilerExtensionVersion,构建就会报错。

解决方式:参考官方 Compose 与 Kotlin 兼容性对照表,保持版本一致。

7. 最佳实践与工程建议

7.1 代码组织与命名

列表页面我建议按“数据模型 → 列表项 UI → 页面组装”三层拆分:

model/ 数据类 Article.kt data/ 数据源 MockData.kt / Repository ui/components/ ArticleItem.kt ui/screens/ ArticleListScreen.kt

这样每个文件职责单一。列表项 UI 只负责展示,页面负责状态管理,数据源负责数据获取。初学者最容易犯的错误是把数据加载、状态管理、列表项 UI 全写在一个 Composable 里,导致一个函数上百行,后期很难维护。

7.2 状态管理

对于列表这类有大量数据交互的场景,我推荐使用ViewModel配合StateFlow来管理列表数据,而不是把所有状态都塞进 Composable。

class ArticleListViewModel : ViewModel() { private val _articles = MutableStateFlow<List<Article>>(emptyList()) val articles: StateFlow<List<Article>> = articles.asStateFlow() fun loadArticles() { viewModelScope.launch { val result = repository.getArticles() _articles.value = result } } }

UI 层用collectAsStateWithLifecycle()收集状态:

@Composable fun ArticleListScreen(viewModel: ArticleListViewModel) { val articles by viewModel.articles.collectAsStateWithLifecycle() LazyColumn { items(articles, key = { it.id }) { article -> ArticleItem(article) } } }

这样做的优点:

  • 数据与 UI 解耦。
  • 配置变更(旋转屏幕)时列表数据不丢失。
  • 方便后续接入分页、缓存、网络重试等能力。

7.3 列表数据的不可变性

在 Compose 中,不可变数据是状态管理的核心原则。线程切换、重组发生时,不可变数据可以避免很多“改了一半”的中间态问题。

具体到列表:

  • 对外暴露的列表尽量使用List<T>,而不是MutableList<T>。
  • 数据更新时创建新列表,比如list + newItem或者list.filter { ... }。
  • 不要在 Composable 内部直接修改数据源。

这不仅是 Compose 的规定,也是函数式编程思路的体现:数据流清晰、可预测、更容易 Debug。

7.4 如何在简历项目中体现 Compose 列表能力

如果你正准备把 Jetpack Compose 的项目经历写进简历,列表是值得好好写的一块。你可以从这几个维度描述:

  • 使用LazyColumn实现高性能列表,通过稳定key保证列表项状态恢复。
  • 结合StateFlow+ViewModel构建列表数据层,实现加载、刷新、分页。
  • 通过sealed class实现多类型 list item 混排,支持 Banner、文章卡片、广告等不同业务块。

这些描述比单纯写“使用 Jetpack Compose 开发 App”更能体现你的工程能力。面试官问到的时候,你也能对应说出上面这些细节,说明你确实实践过。

8. 总结与下一步

这一篇围绕“Jetpack Compose 声明式 UI 开发中的列表”做了完整的拆解。我们重点掌握了:

  • 声明式 UI 与传统 RecyclerView + Adapter 的本质差异。
  • LazyColumn、LazyRow 的基础用法与物品、物品索引的区别。
  • 通过稳定 key 保持列表项状态。
  • 构造了一个文章列表 App,并加入点击、收藏、横滑标签、动态分页等常见需求。
  • 分析了多类型列表项、加载更多、空状态和性能优化思路。
  • 整理了高频问题和排查方式。

如果你把这一篇里的示例代码全部手打一遍,再把“列表项类型混排”和“加载更多”改造成自己的项目,Compose 列表这块基本就真正掌握了。

接下来的学习路线建议:

  1. 继续深入学习 Compose 状态管理:remember、mutableStateOf、StateFlow、Snapshot的区别和适用场景。
  2. 把列表和 Navigation 结合,实现点击 item 跳转详情页。
  3. 学习用 Coil 或 Glide 的 Compose 扩展加载网络图片,让列表更接近真实项目。
  4. 在真实项目中尝试用 Jetpack Compose 重写一个小模块,比如首页列表或消息列表,边写边踩坑,收获会非常大。

如果你在练习中遇到了这里没提到的问题,欢迎在评论区带上具体代码和报错信息,我们一起排查。下一篇教程,我计划把 Compose 中“状态管理”这块内容展开讲,把 remember、rememberSaveable、StateFlow 这些概念串起来。如果感兴趣,可以保持关注。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询