如何将两个请求的返回体加载到基于PagingDataAdapter的同一RecyclerView
问题原因分析
- 逻辑覆盖问题:ViewModel中你先后收集两个接口返回的
PagingData流,后续搜索接口的结果会直接覆盖频道接口的结果赋值给LiveData,LiveData只会保留最后一次的赋值,根本没有实现两个数据集合并的效果。 - 死循环问题:Fragment中在
searchResult的观察回调里又调用了getSearchResult方法,这会再次触发接口请求、重新给LiveData赋值,进而再次触发观察回调,形成无限循环,阻塞了submitData的执行逻辑。 - 方法误用问题:你对
PagingData调用map方法时没有返回转换后的条目,反而在里面执行接口请求,属于对PagingData操作API的误用,会导致数据转换逻辑失效。
可行实现方案
步骤1:定义统一的列表条目类型
首先定义密封类统一两种条目的类型,方便适配器做viewType区分:
sealed class SearchListItem { // 搜索结果条目 data class SearchResultItem(val originData: SearchItems) : SearchListItem() // 频道条目 data class ChannelItem(val originData: Channel) : SearchListItem() }
步骤2:修改ViewModel代码实现PagingData合并
使用combine操作符合并两个流的PagingData,避免后续结果覆盖之前的结果:
private val _searchResult = MutableLiveData<Resource<PagingData<SearchListItem>>>() val searchResult: LiveData<Resource<PagingData<SearchListItem>>> = _searchResult fun getSearchResult(q: String, id: String) = viewModelScope.launch { _searchResult.postValue(Resource.Loading()) combine( // 转换搜索结果的条目类型 repository.getSearch(q).map { pagingData -> pagingData.map { SearchListItem.SearchResultItem(it) } }, // 转换频道结果的条目类型 repository.fetchChannels(id).map { pagingData -> pagingData.map { SearchListItem.ChannelItem(it) } } ) { searchPaging, channelPaging -> // 可根据需求调整合并顺序,此处示例把频道条目放在列表头部 channelPaging.insertFooterItem { searchPaging } }.collect { mergedPagingData -> _searchResult.value = Resource.Success(mergedPagingData) } }
如果你的两个接口都不需要分页、是一次性返回全量数据,也可以直接合并成普通列表再转成PagingData,逻辑更简单:
fun getSearchResult(q: String, id: String) = viewModelScope.launch { _searchResult.postValue(Resource.Loading()) runCatching { val searchList = repository.getSearch(q).first().map { SearchListItem.SearchResultItem(it) } val channelList = repository.fetchChannels(id).first().map { SearchListItem.ChannelItem(it) } val mergedList = channelList + searchList PagingData.from(mergedList) }.onSuccess { _searchResult.value = Resource.Success(it) }.onFailure { _searchResult.value = Resource.Error(it.message ?: "请求失败") } }
步骤3:修改Fragment代码移除死循环逻辑
删除观察回调里的getSearchResult调用,避免死循环:
viewModel.searchResult.observe(viewLifecycleOwner) { when (it) { is Resource.Error -> { it.message?.let { msg -> Log.d("mecal", msg) } } is Resource.Success -> { lifecycleScope.launch { it.data?.let { mergedData -> searchAdapter.submitData(mergedData) } } } is Resource.Loading -> { // 可自行添加加载状态的UI逻辑 } } }
步骤4:适配适配器逻辑
确认你的PagingDataAdapter的getItemViewType方法根据SearchListItem的实际类型返回对应viewType,DiffUtil.ItemCallback正确处理两种条目的差分判断即可。
内容的提问来源于stack exchange,提问作者Radin
相关产品推荐
相关产品推荐

