Paging 3 是 Jetpack 提供的分页加载库,可以高效地加载和显示大量数据。结合 Compose 的 LazyColumn,可以轻松实现无限滚动列表。
一、实现 PagingSource
class ArticlePagingSource(
private val apiService: ApiService
) : PagingSource<Int, Article>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Article> {
val page = params.key ?: 1
return try {
val response = apiService.getArticles(page, params.loadSize)
LoadResult.Page(
data = response.articles,
prevKey = if (page == 1) null else page - 1,
nextKey = if (response.articles.isEmpty()) null else page + 1
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
二、创建 Pager
val articles: Flow<PagingData<Article>> = Pager(
config = PagingConfig(
pageSize = 20,
prefetchDistance = 5,
enablePlaceholders = false
),
pagingSourceFactory = { ArticlePagingSource(apiService) }
).flow.cachedIn(viewModelScope)
三、在 Compose 中使用
@Composable
fun ArticleList(viewModel: ArticleViewModel) {
val articles = viewModel.articles.collectAsLazyPagingItems()
LazyColumn {
items(
count = articles.itemCount,
key = articles.itemKey { it.id }
) { index ->
articles[index]?.let { ArticleCard(it) }
}
// 底部加载状态
when (articles.loadState.append) {
is LoadState.Loading -> {
item { CircularProgressIndicator() }
}
is LoadState.Error -> {
item { ErrorItem(onRetry = { articles.retry() }) }
}
else -> {}
}
}
}
四、RemoteMediator 离线优先
@OptIn(ExperimentalPagingApi::class)
class ArticleRemoteMediator(...) : RemoteMediator<Int, ArticleEntity>() {
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, ArticleEntity>
): MediatorResult {
// 从网络加载并存入数据库
database.withTransaction {
if (loadType == LoadType.REFRESH) {
articleDao.clearAll()
}
articleDao.insertAll(entities)
}
return MediatorResult.Success(endOfPaginationReached)
}
}
五、最佳实践
- ✅ 使用
cachedIn(viewModelScope)缓存 - ✅ 使用
itemKey提供稳定的 key - ✅ 处理所有 LoadState
- ✅ 实现下拉刷新
- ✅ 使用 RemoteMediator 实现离线优先
总结
- PagingSource:定义数据加载逻辑
- collectAsLazyPagingItems():在 Compose 中收集
- LoadState:处理加载状态
- RemoteMediator:离线优先架构