如何在ViewPager2与TabLayoutMediator中实现双向无限滚动
ViewPager2 双向无限滚动实现方案
核心实现逻辑是通过「虚拟超大滚动长度+下标取模映射」模拟无限滚动效果,无需额外监听滚动事件做位置跳转,不会出现滑动卡顿。
修改适配器代码
class LandingViewPagerAdapter(fa: FragmentActivity, val slides: List<DtoSlide>) : FragmentStateAdapter(fa) { // 返回足够大的数值作为虚拟总长度,普通使用场景下完全不会滑到边界 override fun getItemCount(): Int { return if (slides.isEmpty()) 0 else Int.MAX_VALUE } override fun createFragment(position: Int): Fragment { // 用取模运算把虚拟位置映射为实际数据集的下标 val actualPosition = position % slides.size return LandingSlideFragment().newInstance(slides[actualPosition]) } // 新增方法:对外提供实际数据集对应的下标 fun getActualPosition(position: Int): Int { return if (slides.isEmpty()) 0 else position % slides.size } }
修改Activity初始化逻辑
private fun setupViewPager(slides: List<data.network.entities.DtoSlide>) { pagerAdapter = LandingViewPagerAdapter(this, slides) viewpager_landing.adapter = pagerAdapter // 先给TabLayout添加和实际slides数量一致的指示器点 viewpager_dots.removeAllTabs() repeat(slides.size) { viewpager_dots.addTab(viewpager_dots.newTab()) } // 初始定位到虚拟长度的中间位置,保证左右都有足够的滚动空间 if (slides.isNotEmpty()) { val midPosition = Int.MAX_VALUE / 2 - (Int.MAX_VALUE / 2 % slides.size) viewpager_landing.setCurrentItem(midPosition, false) } // 适配TabLayout的指示器 TabLayoutMediator(viewpager_dots, viewpager_landing, false, false) { tab, position -> // 已提前创建好对应数量的tab,此处无需额外配置 }.attach() // 添加滚动监听同步Tab选中状态,避免指示器错位 viewpager_landing.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { super.onPageSelected(position) val actualPos = pagerAdapter.getActualPosition(position) if (viewpager_dots.selectedTabPosition != actualPos) { viewpager_dots.getTabAt(actualPos)?.select() } } }) }
额外注意事项
- 如果你的slides数量小于等于1,建议先做非空判断屏蔽滚动逻辑,避免无意义的适配
- 该方案适配Fragment、View等所有ViewPager2的适配场景,性能和普通滚动完全一致
- 不需要额外处理滑动边界事件,Int.MAX_VALUE约等于21亿次滑动,正常使用完全不会触达边界
内容的提问来源于stack exchange,提问作者Mario Muresean
相关产品推荐
相关产品推荐

