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
相关产品推荐
相关产品推荐

