Paging3切换分类时RecyclerView短暂显示旧数据问题求助
问题描述
UI包含RecyclerView和ChipGroup,每个Chip对应不同的Retrofit请求。点击Chip时,期望RecyclerView在新数据加载完成前隐藏,实际确实隐藏了,但重新显示时会先短暂展示旧数据,之后才更新为新数据。
Fragment代码实现
分页数据与加载状态观察
viewLifecycleOwner.lifecycleScope.launch { pagingData.collectLatest(customAdapter::submitData) } viewLifecycleOwner.lifecycleScope.launch { customAdapter.loadStateFlow.collect { loadState -> header.loadState = loadState.mediator ?.refresh ?.takeIf { it is LoadState.Error && customAdapter.itemCount > 0 } ?: loadState.prepend val isListEmpty = loadState.refresh is LoadState.NotLoading && customAdapter.itemCount == 0 emptyList.isVisible = isListEmpty recyclerView.isVisible = loadState.mediator?.refresh is LoadState.NotLoading || loadState.source.refresh is LoadState.NotLoading progressBar.isVisible = loadState.mediator?.refresh is LoadState.Loading retryButton.isVisible = loadState.mediator?.refresh is LoadState.Error && customAdapter.itemCount == 0 val errorState = loadState.source.append as? LoadState.Error ?: loadState.source.prepend as? LoadState.Error ?: loadState.append as? LoadState.Error ?: loadState.prepend as? LoadState.Error errorState?.let { Toast.makeText( requireContext(), "😨 Wooops ${it.error}", Toast.LENGTH_LONG ).show() } } }
分类切换逻辑
chip1.setOnClickListener { viewLifecycleOwner.lifecycleScope.launch { onCategoryChanged(CustomAction.ChangeCategory(category = CustomCategory.A)) } } chip2.setOnClickListener { viewLifecycleOwner.lifecycleScope.launch { customAdapter.refresh() onCategoryChanged(CustomAction.ChangeCategory(category = CustomCategory.B)) } }
ViewModel代码实现
val actionStateFlow = MutableSharedFlow<CustomAction>() val changedCategory = actionStateFlow .filterIsInstance<CustomAction.ChangeCategory>() .distinctUntilChanged() .onStart { emit(CustomAction.ChangeCategory(category = initialCategory)) } pagingDataFlow = state .flatMapLatest { when (it.category) { CustomCategory.A -> searchA(query = it.query) CustomCategory.B -> searchB(query = it.query) } } .cachedIn(viewModelScope) private fun searchA(query: String): Flow<PagingData<CustomItem>> { return repository.getA(query = query).map { pagingData -> pagingData.map { it.mapToDomainModel() } } } private fun searchB(query: String): Flow<PagingData<CustomItem>> { return repository.getB(query = query).map { pagingData -> pagingData.map { it.mapToDomainModel() } } }
修复方案
1. 移除多余的customAdapter.refresh()调用
切换分类时,ViewModel会通过flatMapLatest自动切换到新的分页数据流,collectLatest会自动提交新的PagingData到Adapter。手动调用refresh()会触发旧数据流的刷新操作,导致旧数据残留闪烁。修改Chip点击逻辑:
chip2.setOnClickListener { viewLifecycleOwner.lifecycleScope.launch { onCategoryChanged(CustomAction.ChangeCategory(category = CustomCategory.B)) } }
2. 优化RecyclerView显示控制逻辑
当前的显示条件会在刷新状态变为NotLoading时立即显示RecyclerView,但此时新数据可能还未完全替换旧数据。调整逻辑,确保只有在刷新完成且有新数据时才显示RecyclerView:
viewLifecycleOwner.lifecycleScope.launch { customAdapter.loadStateFlow.collect { loadState -> header.loadState = loadState.mediator ?.refresh ?.takeIf { it is LoadState.Error && customAdapter.itemCount > 0 } ?: loadState.prepend val isRefreshing = loadState.mediator?.refresh is LoadState.Loading || loadState.source.refresh is LoadState.Loading val isRefreshCompleted = loadState.mediator?.refresh is LoadState.NotLoading || loadState.source.refresh is LoadState.NotLoading val hasData = customAdapter.itemCount > 0 val isListEmpty = isRefreshCompleted && !hasData emptyList.isVisible = isListEmpty // 仅当刷新完成且有数据时才显示RecyclerView recyclerView.isVisible = !isRefreshing && isRefreshCompleted && hasData progressBar.isVisible = isRefreshing retryButton.isVisible = (loadState.mediator?.refresh is LoadState.Error || loadState.source.refresh is LoadState.Error) && !hasData val errorState = loadState.source.append as? LoadState.Error ?: loadState.source.prepend as? LoadState.Error ?: loadState.append as? LoadState.Error ?: loadState.prepend as? LoadState.Error errorState?.let { Toast.makeText( requireContext(), "😨 Wooops ${it.error}", Toast.LENGTH_LONG ).show() } } }
3. 确保数据流切换的原子性
ViewModel中使用flatMapLatest是正确的,它会在新的分类状态到来时立即取消旧的分页数据流请求,避免旧数据干扰。如果你的state流是基于changedCategory生成的,确保scan或其他转换逻辑正确,保证每次分类变化都会生成全新的状态对象,触发flatMapLatest切换数据流。
内容的提问来源于stack exchange,提问作者ParSerDev
相关产品推荐
相关产品推荐

