0Pricing
Android Academy · 课时

在 Compose 列表中实现分页

使用 LazyColumn 渲染分页数据

在 Compose 列表中实现分页 是 CoddyKit 上的免费 Android Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Android Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Android Academy 课程共包含 4 节课。

在 Compose 中呈现分页数据

数据层会提供一个 Flow<PagingData<Article>>。paging-compose 库会将这个数据流转换为 LazyColumn 可以呈现的对象,并在用户滚动时按需加载数据。

其中的关键桥接函数是 collectAsLazyPagingItems()。

collectAsLazyPagingItems

在可组合函数中对数据流调用 collectAsLazyPagingItems()。它会返回一个 LazyPagingItems 对象,用于跟踪已加载的项目和加载状态,并在页面到达时重新组合界面。

import androidx.paging.compose.collectAsLazyPagingItems

@Composable
fun ArticleScreen(viewModel: ArticleViewModel) {
    val articles = viewModel.articles.collectAsLazyPagingItems()
    ArticleList(articles)
}

使用 LazyPagingItems 调用 items()

在 LazyColumn 中使用 Paging 的 items() 重载。它会从 LazyPagingItems 读取数据,并在用户接近列表末尾时触发下一页的加载。

启用占位符后,每个项目都可能是 null,因此请处理这种情况。

import androidx.compose.foundation.lazy.LazyColumn
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.items

@Composable
fun ArticleList(articles: LazyPagingItems<Article>) {
    LazyColumn {
        items(articles) { article ->
            if (article != null) {
                ArticleRow(article)
            }
        }
    }
}

为项目设置稳定键

为每一行提供稳定键,以便 Compose 在页面加载过程中高效跟踪项目,并避免不必要的重新组合。

请使用 Paging 的 itemKey 辅助工具,并传入项目 ID 之类唯一且稳定的属性。

import androidx.paging.compose.itemKey

LazyColumn {
    items(
        count = articles.itemCount,
        key = articles.itemKey { it.id }
    ) { index ->
        val article = articles[index]
        if (article != null) ArticleRow(article)
    }
}

读取加载状态

LazyPagingItems.loadState 会公开以下三种操作当前的加载状态和错误状态:

  • refresh - 初始加载或下拉刷新加载
  • append - 加载下一页(向下滚动)
  • prepend - 加载上一页(向上滚动)
import androidx.paging.LoadState

val refresh = articles.loadState.refresh
val append = articles.loadState.append

val isInitialLoading = refresh is LoadState.Loading
val isLoadingMore = append is LoadState.Loading

全屏加载状态和空状态

使用 refresh 状态来控制初始界面:加载时显示居中的加载动画,失败时显示错误界面,没有可显示内容时显示空状态消息。

when (val refresh = articles.loadState.refresh) {
    is LoadState.Loading -> FullScreenSpinner()
    is LoadState.Error -> FullScreenError(
        message = refresh.error.message,
        onRetry = { articles.retry() }
    )
    is LoadState.NotLoading -> {
        if (articles.itemCount == 0) EmptyState()
        else ArticleList(articles)
    }
}

追加数据时显示底部加载动画

要在加载下一页时于底部显示小型加载动画,请根据 append 加载状态向 LazyColumn 添加一个额外项目。

LazyColumn {
    items(count = articles.itemCount, key = articles.itemKey { it.id }) { index ->
        articles[index]?.let { ArticleRow(it) }
    }

    if (articles.loadState.append is LoadState.Loading) {
        item {
            Box(Modifier.fillMaxWidth().padding(16.dp), Alignment.Center) {
                CircularProgressIndicator()
            }
        }
    }
}

发生错误时重试

追加数据失败时,请在底部显示重试行。调用 articles.retry() 只会重新尝试失败的加载,不会重新加载整个列表。

val append = articles.loadState.append
if (append is LoadState.Error) {
    item {
        Row(Modifier.fillMaxWidth().padding(16.dp), Arrangement.Center) {
            Text("Couldn't load more")
            Spacer(Modifier.width(8.dp))
            Button(onClick = { articles.retry() }) { Text("Retry") }
        }
    }
}

下拉刷新

使用 articles.refresh() 触发全新加载。可以将它与 Material 3 的下拉刷新结合使用,并根据 refresh 加载状态控制指示器。

import androidx.compose.material3.pulltorefresh.PullToRefreshBox

val isRefreshing = articles.loadState.refresh is LoadState.Loading
PullToRefreshBox(
    isRefreshing = isRefreshing,
    onRefresh = { articles.refresh() }
) {
    ArticleList(articles)
}

页眉与分隔线

您可以在同一个 LazyColumn 中自由混合分页项目和非分页内容。在分页 items() 调用周围添加粘性页眉、搜索栏或分区分隔线。

由于所有内容都位于同一个 LazyColumn 中,滚动会保持统一且流畅。

LazyColumn {
    item { SearchBar(query, onQueryChange) }

    items(count = articles.itemCount, key = articles.itemKey { it.id }) { index ->
        val article = articles[index]
        if (article != null) {
            ArticleRow(article)
            HorizontalDivider()
        }
    }

    if (articles.loadState.append.endOfPaginationReached) {
        item { Text("You're all caught up", Modifier.padding(16.dp)) }
    }
}

预览分页列表

对于 Compose 预览和测试,不需要真实的网络。请将示例数据包装在 PagingData.from(...) 中,并将其公开为数据流。

这样,您就可以独立构建和预览列表界面。

import androidx.paging.PagingData
import kotlinx.coroutines.flow.flowOf

@Preview
@Composable
fun ArticleListPreview() {
    val sample = listOf(Article(1, "Hello"), Article(2, "World"))
    val flow = flowOf(PagingData.from(sample))
    ArticleList(flow.collectAsLazyPagingItems())
}

快速检查

哪个函数可以将 Flow<PagingData> 转换为 Compose LazyColumn 能够呈现的对象?

回顾:Compose 中的分页

您已经在 Jetpack Compose 中呈现了分页数据:

  • collectAsLazyPagingItems() 将数据流连接到界面
  • Paging 的 items() 重载会在滚动时加载更多数据;请处理 null 占位项目
  • 使用 itemKey { it.id } 设置稳定键
  • loadState.refresh/append/prepend 用于控制加载动画、错误状态和空状态
  • retry() 和 refresh() 用于从失败中恢复并重新加载

下一步:使用 RemoteMediator 结合网络和数据库。

常见问题解答

「在 Compose 列表中实现分页」课时是免费的吗?

是的 — 「在 Compose 列表中实现分页」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Android Academy 课程的其余内容,请升级到 CoddyKit PRO。 Android Academy 课程共包含 4 节课。

「在 Compose 列表中实现分页」这节课中我会学到什么?

使用 LazyColumn 渲染分页数据 你通过在浏览器中直接运行的动手代码来练习 Android Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Android Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Android Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「在 Compose 列表中实现分页」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Android Academy 课中编写并运行代码吗?

能。每节 Android Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 为什么需要分页
  2. PagingSource 与 Pager
  3. 在 Compose 列表中实现分页
  4. RemoteMediator 与缓存
← 返回 Android Academy