Android Sunflower Paging 3高级用法:自定义加载状态
你还在为Paging 3加载状态处理烦恼吗?本文以Sunflower项目为例,详细讲解如何在Jetpack Compose中实现自定义加载状态,包括加载中、错误处理和空数据展示。读完本文你将掌握:
- PagingSource自定义实现
- ViewModel中的状态管理
- Compose UI加载状态处理
- 下拉刷新与错误重试机制
Paging 3架构概览
Paging 3是Android Jetpack组件库中用于高效加载分页数据的解决方案。在Sunflower项目中,图片画廊功能通过Paging 3实现网络图片的分页加载,核心实现位于以下文件:
- 数据源:app/src/main/java/com/google/samples/apps/sunflower/data/UnsplashPagingSource.kt
- 状态管理:app/src/main/java/com/google/samples/apps/sunflower/viewmodels/GalleryViewModel.kt
- UI展示:app/src/main/java/com/google/samples/apps/sunflower/compose/gallery/GalleryScreen.kt
自定义PagingSource实现
PagingSource是数据源的核心,负责定义如何从网络或数据库加载数据。Sunflower项目中的UnsplashPagingSource实现了从Unsplash API加载图片的逻辑:
class UnsplashPagingSource(
private val service: UnsplashService,
private val query: String
) : PagingSource<Int, UnsplashPhoto>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, UnsplashPhoto> {
val page = params.key ?: UNSPLASH_STARTING_PAGE_INDEX
return try {
val response = service.searchPhotos(query, page, params.loadSize)
LoadResult.Page(
data = response.results,
prevKey = if (page == UNSPLASH_STARTING_PAGE_INDEX) null else page - 1,
nextKey = if (page == response.totalPages) null else page + 1
)
} catch (exception: Exception) {
LoadResult.Error(exception) // 错误状态处理
}
}
}
关键实现说明:
- 分页参数:通过
params.key获取当前页码,默认为起始页 - 数据加载:调用
UnsplashService获取分页数据 - 状态返回:成功时返回
LoadResult.Page,错误时返回LoadResult.Error
ViewModel中的状态管理
GalleryViewModel负责将Paging数据流转换为UI可观察的状态:
class GalleryViewModel @Inject constructor(
private val repository: UnsplashRepository
) : ViewModel() {
private val _plantPictures = MutableStateFlow<PagingData<UnsplashPhoto>?>(null)
val plantPictures: Flow<PagingData<UnsplashPhoto>> get() = _plantPictures.filterNotNull()
fun refreshData() {
viewModelScope.launch {
_plantPictures.value = repository.getSearchResultStream(queryString ?: "").cachedIn(viewModelScope).first()
}
}
}
核心逻辑:
- 使用
MutableStateFlow存储分页数据 - 通过
cachedIn(viewModelScope)确保配置变更时数据不丢失 - 提供
refreshData()方法支持手动刷新
Compose UI加载状态处理
GalleryScreen实现了完整的加载状态UI,包括下拉刷新和错误处理:
@Composable
private fun GalleryScreen(
plantPictures: Flow<PagingData<UnsplashPhoto>>,
onPullToRefresh: () -> Unit,
) {
val pagingItems: LazyPagingItems<UnsplashPhoto> = plantPictures.collectAsLazyPagingItems()
val pullToRefreshState = rememberPullToRefreshState()
LaunchedEffect(pagingItems.loadState) {
when (pagingItems.loadState.refresh) {
is LoadState.Loading -> Unit // 下拉刷新中
is LoadState.Error, LoadState.NotLoading -> pullToRefreshState.endRefresh()
}
}
Box(modifier = Modifier.nestedScroll(pullToRefreshState.nestedScrollConnection)) {
LazyVerticalGrid(columns = GridCells.Fixed(2)) {
items(count = pagingItems.itemCount) { index ->
val photo = pagingItems[index] ?: return@items
PhotoListItem(photo = photo)
}
}
PullToRefreshContainer(
modifier = Modifier.align(Alignment.TopCenter),
state = pullToRefreshState
)
}
}
加载状态处理策略
| 状态类型 | 处理方式 | 对应代码位置 |
|---|---|---|
| 加载中 | 显示下拉刷新动画 | GalleryScreen.kt#L89-L91 |
| 加载完成 | 隐藏刷新动画并更新列表 | GalleryScreen.kt#L96-L103 |
| 加载错误 | 记录异常并允许重试 | UnsplashPagingSource.kt#L40-L42 |
高级扩展:错误状态自定义
要实现更友好的错误提示,可以扩展LoadState.Error处理逻辑:
when (val state = pagingItems.loadState.refresh) {
is LoadState.Error -> {
ErrorView(
message = state.error.localizedMessage,
onRetry = { pagingItems.retry() }
)
}
}
建议在项目中添加错误视图组件,位置参考:app/src/main/java/com/google/samples/apps/sunflower/compose/utils/
总结与最佳实践
- 状态隔离:通过ViewModel分离UI状态与数据加载逻辑
- 错误边界:在PagingSource中捕获所有网络异常
- 用户体验:始终提供加载状态反馈和重试机制
- 性能优化:使用
cachedIn(viewModelScope)避免重复请求
完整实现可参考Sunflower项目的Gallery模块,建议结合官方文档Paging 3指南深入学习。
点赞收藏本文,下期将带来"Paging 3与Room数据库结合实战",敬请关注!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考





