如何在Compose中使用Paging 3库实现点击按钮加载下一页数据
使用Paging 3实现点击按钮加载下一页电影
要实现用户点击按钮才加载下一页的需求,我们需要修改Paging的自动加载逻辑,改为手动触发。以下是基于你现有代码的改造方案:
1. 修改PagingDataSource,添加手动触发加载的信号
在MoviesPagingDataSource中加入信号流,只有收到手动触发信号时,才会执行下一页加载:
class MoviesPagingDataSource( private val repo: MoviesRepository, private val loadNextSignal: MutableSharedFlow<Unit> = MutableSharedFlow() ) : PagingSource<Int, Movies>() { // 暴露手动触发加载的方法 suspend fun triggerLoadNext() { loadNextSignal.emit(Unit) } override fun getRefreshKey(state: PagingState<Int, Movies>): Int? { return state.anchorPosition?.let { anchorPosition -> val anchorPage = state.closestPageToPosition(anchorPosition) anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) } } override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Movies> { return try { val nextPageNumber = params.key ?: 0 // 如果是加载下一页,等待手动触发信号 if (params is LoadParams.Append) { loadNextSignal.first() } val response = repo.getMovies(page = nextPageNumber, size = 10) LoadResult.Page( data = response.content, prevKey = null, nextKey = if (response.content.isNotEmpty()) response.number + 1 else null ) } catch (e: Exception) { LoadResult.Error(e) } } }
2. 改造ViewModel,管理加载触发逻辑
在ViewModel中初始化PagingDataSource,并暴露触发加载下一页的方法:
@HiltViewModel class MoviesViewModel @Inject constructor( private val moviesRepository: MoviesRepository ) : ViewModel() { private val loadNextSignal = MutableSharedFlow<Unit>() private val moviesDataSource = MoviesPagingDataSource(moviesRepository, loadNextSignal) // 供UI观察的分页数据流 val moviesPagingFlow = Pager(PagingConfig(pageSize = 10)) { moviesDataSource }.flow.cachedIn(viewModelScope) // 触发加载下一页 fun loadNextPage() { viewModelScope.launch { moviesDataSource.triggerLoadNext() } } }
3. 更新UI,替换自动加载为手动按钮
移除原来的自动加载逻辑,改为显示"加载更多"按钮,点击时调用ViewModel的触发方法:
@Composable fun MoviesList(viewModel: MoviesViewModel) { val moviesList = viewModel.moviesPagingFlow.collectAsLazyPagingItems() LazyColumn { items(moviesList) { item -> item?.let { MoviesCard(movie = it) } } // 加载状态与加载更多按钮 item { when { // 正在加载下一页 moviesList.loadState.append is LoadState.Loading -> { LoadingItem() } // 加载失败,显示重试按钮 moviesList.loadState.append is LoadState.Error -> { val error = moviesList.loadState.append as LoadState.Error ErrorItem(message = error.error.message ?: "加载失败") { moviesList.retry() } } // 还有下一页,显示加载更多按钮 !moviesList.loadState.append.endOfPaginationReached && moviesList.itemCount > 0 -> { Button( onClick = { viewModel.loadNextPage() }, modifier = Modifier.fillMaxWidth().padding(16.dp) ) { Text("加载更多") } } } } // 初始刷新状态处理 when (moviesList.loadState.refresh) { is LoadState.Loading -> { item { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { CircularProgressIndicator() } } } is LoadState.Error -> { item { ErrorItem(message = "加载失败") { moviesList.refresh() } } } else -> Unit } } }
核心逻辑说明
- 通过
MutableSharedFlow给PagingDataSource添加阻塞信号,只有UI点击按钮发送信号后,下一页加载才会执行。 - 保留Paging库的错误重试、状态管理、数据缓存等原生能力,同时完全由用户掌控加载时机。
- 通过
endOfPaginationReached判断是否还有更多数据,自动隐藏加载按钮。
内容的提问来源于stack exchange,提问作者Breens Mbaka
相关产品推荐
相关产品推荐

