You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何合并两路PagingData分页数据流后在RecyclerView中展示

问题描述

当前使用RecyclerView配合PagingDataAdapter展示分页数据,单次服务端请求场景下功能运行正常,但部分业务场景需要发起两次请求分别获取新旧事件,要求两类事件同时展示在RecyclerView中。现有实现下发起两次请求时,submitData()方法内部会调用invalidate(),最终仅会保留最后一次请求的数据,无法满足两路分页数据流合并后展示的需求,且项目中无法使用Room组件。

现有无法满足需求的实现代码

private fun fetchEvents() {
    fetchEventsJob?.cancel()
    binding.cameraVmsEventsRecycler.adapter = null

    fetchEventsJob = lifecycleScope.launch {
        cameraViewModel.searchCameraEvents(
            cameraId = videoDetector.id,
            dateTo = if(eventsByDate) "" else dayPickerDateTo,
            pageSize = REQUEST_LIMIT,
            cameraViewModel.filtersApplied
        ).collectLatest { eventsCollection ->
            val events = eventsCollection.map { event ->
                CameraEventModel.CameraEventItem(
                    VideoEventModel(
                        event.eventId,
                        event.faceId,
                        null,
                        event.state
                    )
                )
            }.insertSeparators { before: CameraEventModel.CameraEventItem?, after: CameraEventModel.CameraEventItem? ->
                renderSeparators(before, after)
            }
            binding.cameraEventsRecycler.adapter = eventsAdapter
            eventsAdapter.submitData(events)
        }
    }
}

当传入不同参数调用fetchEvents()时,受submitData()的机制限制,仅最后一路数据流的数据会被保留。

实现方案

核心思路是不要分别对两路PagingData调用submitData,而是在数据层先将两路分页源合并为单个PagingData流,再统一提交给适配器,从根源上避免多次调用submitData触发的invalidate逻辑。

方案1:使用Paging官方提供的合并API

Paging 3 原生支持分页数据流合并,不需要依赖Room组件:

  • 首先分别构造两路独立的Pager实例,对应新旧事件的分页请求逻辑,两路请求的分页参数、过滤条件独立维护
  • 调用PagingData.merge()方法合并两路PagingData流,该方法会同时监听多个分页源的加载状态,将加载到的数据按传入顺序拼接为同一份数据流
  • 对合并后的PagingData统一执行map数据转换、insertSeparators插入分隔符等操作,最后仅调用一次submitData()提交给适配器即可

核心实现示例:

// 1. 构造两路独立的分页流
val newEventsFlow = Pager(PagingConfig(pageSize = REQUEST_LIMIT)) {
    NewEventsPagingSource(cameraId, filtersApplied)
}.flow.cachedIn(viewModelScope)

val oldEventsFlow = Pager(PagingConfig(pageSize = REQUEST_LIMIT)) {
    OldEventsPagingSource(cameraId, dateTo, filtersApplied)
}.flow.cachedIn(viewModelScope)

// 2. 合并流,统一处理转换后提交
lifecycleScope.launch {
    PagingData.merge(
        newEventsFlow,
        oldEventsFlow
    ).collectLatest { mergedPagingData ->
        val displayData = mergedPagingData
            .map { event ->
                CameraEventModel.CameraEventItem(
                    VideoEventModel(event.eventId, event.faceId, null, event.state)
                )
            }
            .insertSeparators { before, after -> renderSeparators(before, after) }
        eventsAdapter.submitData(displayData)
    }
}

注意事项:

  • 两路PagingSource的返回数据类型必须一致,如果两类事件字段有差异,先在各自的转换逻辑里映射为统一的CameraEventItem类型再合并
  • 如果需要自定义两类数据的展示顺序,调整merge()方法传入流的顺序即可,先传入的流数据会排在列表前部
  • 合并后的流会自动处理两路分页的加载状态,下拉刷新、上拉加载的逻辑不需要额外修改,适配器的loadStateFlow会同时反映两路源的加载状态

方案2:自定义Mediator实现可控合并

如果需要更灵活的合并逻辑(比如新旧数据按时间穿插排序、动态增删数据源),可以自定义实现类管控数据合并流程:

  • 定义一个内存级的PagingSource,从维护的全局事件缓存列表中按分页参数截取对应位置的数据返回
  • 在分页触发节点(刷新、加载更多)同时发起新旧事件的接口请求,将返回结果去重、按业务规则排序后更新到全局事件缓存列表中,再通知PagingSource数据变更
  • 整个流程中始终只维护一个Pager实例,只需要调用一次submitData(),完全不会触发多次invalidate的问题

常见踩坑点

  • 不要在collectLatest中重复给RecyclerView设置adapter,adapter只需要在视图初始化阶段绑定一次即可,重复设置会导致列表状态丢失、滚动位置重置
  • 两路分页流的PagingConfig参数最好保持一致,避免pageSize不一致导致的加载逻辑异常
  • 合并后的数据如果需要支持下拉刷新,直接调用adapter.refresh()即可,merge操作会自动触发两路源的刷新逻辑

内容的提问来源于stack exchange,提问作者dazai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 04:18:14