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

Paging 3库中点击项加载更多并向RecyclerView追加数据如何实现

问题核心原因

你当前的实现存在两个核心问题导致数据被替换:

  1. 每次点击Load more按钮都重新创建了PagingData流并调用submitData,Adapter会将新的PagingData识别为全新数据集,直接覆盖旧数据
  2. PagingSource中硬编码返回nextKey = null,Paging3无法识别还有下一页数据,内部分页状态管理失效

具体修改步骤

1. 调整Paging配置,禁用自动预加载(适配点击才加载的需求)

你需要在构造Pager时指定PagingConfig,关闭滚动自动预加载的逻辑:

val pagingConfig = PagingConfig(
    pageSize = 50, // 你设置的单页数据量
    prefetchDistance = 0, // 禁用预加载,滚动到底部不会自动触发下页请求
    enablePlaceholders = false,
    initialLoadSize = 50
)

2. 修正PagingSource逻辑,正确返回分页key

不要硬写nextKey = null,也不要用外部的pagingTasksInput.pageNumber覆盖Paging内部维护的页码,让Paging3自行管理分页状态:

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, TaskDto> {
    val pageNumber = params.key ?: DEFAULT_PAGE_INDEX
    return try {
        val response = tasksApi.getTasks(
            token = "Bearer " + NicoPrefs.getSSOAccessToken(),
            status = pagingTasksInput.status,
            type = pagingTasksInput.type,
            activity = pagingTasksInput.activity,
            sort = pagingTasksInput.sort,
            sortBy = pagingTasksInput.sortBy,
            pageNumber = pageNumber, // 直接用Paging内部维护的页码
            pageLimit = pagingTasksInput.pageLimit,
            assignee = pagingTasksInput.assignee
        )
        val data = response.result.orEmpty()
        // 根据返回数据量判断是否还有下一页,也可以用接口返回的总页数/是否为最后一页字段判断
        val nextKey = if (data.size == pagingTasksInput.pageLimit) pageNumber + 1 else null

        LoadResult.Page(
            data = data,
            prevKey = if (pageNumber == DEFAULT_PAGE_INDEX) null else pageNumber - 1,
            nextKey = nextKey // 正确返回下一页页码
        )
    } catch (exception: IOException) {
        LoadResult.Error(exception)
    } catch (exception: HttpException) {
        LoadResult.Error(exception)
    }
}

3. ViewModel中只初始化一次Paging流

不要每次点击都重新生成Paging流,将流缓存到ViewModel中复用:

class TestViewModel : ViewModel() {
    // 只在ViewModel初始化时创建一次Paging流,全局复用
    val pagingTasksFlow = Pager(
        config = pagingConfig,
        pagingSourceFactory = { TasksPagingSource(pagingTasksInput) }
    ).flow
        .map {
            it.insertFooterItem(TaskDto(footer = "FOOTER", description = "Load more"))
        }
        .cachedIn(viewModelScope) // 缓存流,避免页面重建重新加载
}

4. 页面端只收集一次Paging流

将流收集逻辑移到onViewCreated中,只执行一次,不要放到点击事件中:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // 只初始化一次流收集
    viewLifecycleOwner.lifecycleScope.launch {
        viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
            testViewModel.pagingTasksFlow.collectLatest { pagingData ->
                assignedTasksPagingAdapter?.submitData(pagingData)
            }
        }
    }
}

5. 点击LoadMore按钮触发下页加载

在你的页脚按钮点击事件中加入触发加载的逻辑即可,Paging3会自动将新请求到的数据追加到原有列表后:

// 页脚Load more按钮点击回调
fun onLoadMoreClick() {
    assignedTasksPagingAdapter?.let { adapter ->
        val appendState = adapter.loadState.append
        // 仅当还有下页且没有在加载中时触发请求
        if (appendState is LoadState.NotLoading && !appendState.endOfPaginationReached) {
            adapter.retry()
        }
    }
}

补充说明

只要你正确复用同一个Paging流,Paging3内部会自动维护所有已加载的分页数据,新加载的页会自动追加到列表尾部,submitData方法只会做增量更新,不会全量替换原有数据。同时需要确保你的PagingDataAdapter的DiffUtil.ItemCallback正确实现了新旧项的对比逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 01:15:06