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

Paging 3 PagingSource的itemBefore/itemAfter用法及ViewPager2竞速问题咨询

解决方案

1. 修复Paging基础配置

首先禁用占位符,双向无限滚动场景下没有确定的总item数量,占位符会导致ViewPager2的item计数错乱。修改ViewPagerViewModel.kt中的Pager配置:

val dataList =
    Pager(config = PagingConfig(
        pageSize = 10,
        enablePlaceholders = false // 新增:禁用占位符
    ), pagingSourceFactory = {
        ViewPagerPagingSource(dataSource)
    }).flow

2. 完善PagingSource实现

修正getRefreshKey的逻辑,保证刷新时停留在当前浏览页面,不需要修改itemsBefore和itemsAfter(禁用占位符后这两个属性无意义,保持默认的COUNT_UNDEFINED即可):

class ViewPagerPagingSource(
    private val dataSource: ViewPagerDataSource
) : PagingSource<Int, Date>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Date> {
        // 原有load逻辑保持不变即可
        val position = params.key ?: 0
        return try {
            val data = dataSource.loadDataSource(position)
            LoadResult.Page(
                data = data,
                prevKey = if (data.isEmpty()) null else position - 1,
                nextKey = if (data.isEmpty()) null else position + 1,
                itemsBefore = LoadResult.Page.COUNT_UNDEFINED,
                itemsAfter = LoadResult.Page.COUNT_UNDEFINED
            )
        } catch (exception: IOException) {
            LoadResult.Error(exception)
        }
    }

    override fun getRefreshKey(state: PagingState<Int, Date>): Int? {
        // 修改为返回当前可见页附近的key,刷新后不跳回初始页
        return state.anchorPosition?.let { anchorPosition ->
            state.closestPageToPosition(anchorPosition)?.prevKey 
                ?: state.closestPageToPosition(anchorPosition)?.nextKey
        }
    }
}

3. 给按钮添加滑动防抖

快速点击时上一次滑动动画未完成,currentItem还未更新,会导致position计算错误。可以通过限制点击间隔或监听ViewPager2滚动状态解决,修改MainActivity.kt的点击逻辑:

class MainActivity : AppCompatActivity() {
    private val viewModel by viewModels<ViewPagerViewModel>()
    private lateinit var binding : ActivityMainBinding
    // 新增:控制按钮可点击状态
    private var isPageClickable = true
    private val scrollDuration = 300L // 和ViewPager2默认滑动动画时长对齐

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val adapter = ViewPagerAdapter()
        lifecycleScope.launch {
            viewModel.dataList.collectLatest {
                adapter.submitData(it)
            }
        }
        binding.viewpager.adapter = adapter

        binding.next.setOnClickListener {
            if (isPageClickable) {
                isPageClickable = false
                binding.viewpager.setCurrentItem(binding.viewpager.currentItem + 1, true)
                it.postDelayed({ isPageClickable = true }, scrollDuration)
            }
        }

        binding.previous.setOnClickListener {
            if (isPageClickable) {
                isPageClickable = false
                binding.viewpager.setCurrentItem(binding.viewpager.currentItem - 1, true)
                it.postDelayed({ isPageClickable = true }, scrollDuration)
            }
        }

        // 可选:监听滚动状态兜底,避免滑动被打断时按钮长期不可点击
        binding.viewpager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageScrollStateChanged(state: Int) {
                super.onPageScrollStateChanged(state)
                if (state == ViewPager2.SCROLL_STATE_IDLE) {
                    isPageClickable = true
                }
            }
        })
    }
}
概念说明
  • itemsBefore/itemsAfter:仅在开启占位符时需要配置,分别代表当前页数据前后未加载的item数量。双向无限滚动场景没有固定边界,无法计算准确值,直接禁用占位符即可。
  • getRefreshKey:用于PagingSource刷新时确定起始加载位置,返回null会导致刷新后回到初始加载页,上面的实现可以保证刷新后停留在当前浏览的页面。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 09:09:03