为什么Android ViewPager接入API数据后无限轮播功能失效?
问题根源
- 数据不同步:你同时维护了
ListAdapter内部自带的数据集和适配器中自定义的info列表,二者完全没有同步。本地测试时你提前给全局data赋值再初始化适配器,info能拿到正确值,但是API异步返回数据后,你只调用了submitList更新ListAdapter内部数据集,没有同步到自定义的info,info一直是空值,onBindViewHolder里的判断条件position == info.size - 2永远不会触发,自然不会执行复制列表的无限轮播逻辑。 - 观察者逻辑错误:API返回数据后你调用
adapter.addAll(data),这里传入的data是初始化时创建的空列表,实际API返回的结果存在it参数里,等于你根本没把接口数据传给适配器自定义的info。 - 无限轮播实现不合理:通过反复
addAll(info)复制列表会导致数据集长度无限翻倍,滑动次数多了会触发OOM,完全没必要这么实现。
修复方案
直接重构无限轮播逻辑,改用ViewPager2通用的无限轮播实现,不需要复制列表,性能更稳定:
- 改造
CarouselItemAdapter,删除自定义的info列表、runnable、addAll方法,完全用ListAdapter自带的currentList获取数据:
class CarouselItemAdapter internal constructor(private val viewPager2: ViewPager2) : ListAdapter<CarouselItem,CarouselItemAdapter.CarouselViewHolder>(DiffCallback) { companion object DiffCallback : DiffUtil.ItemCallback<CarouselItem>() { override fun areItemsTheSame(oldItem: CarouselItem, newItem: CarouselItem): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: CarouselItem, newItem: CarouselItem): Boolean { return oldItem == newItem } } override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): CarouselViewHolder { return CarouselViewHolder(CustomContainerBinding.inflate( LayoutInflater.from(parent.context), parent, false)) } override fun onBindViewHolder(holder: CarouselViewHolder, position: Int) { // 取模计算真实数据下标 val realPosition = position % currentList.size holder.bind(currentList[realPosition]) } // 返回最大值实现无限滑动 override fun getItemCount(): Int { return if (currentList.isEmpty()) 0 else Int.MAX_VALUE } class CarouselViewHolder(private val binding: CustomContainerBinding) : RecyclerView.ViewHolder(binding.root){ fun bind(item: CarouselItem){ binding.parts = item binding.executePendingBindings() } } }
- 修正Fragment中的初始化和观察者逻辑:
// 初始化适配器不再传空列表 val adapter = CarouselItemAdapter(viewPager) viewPager.adapter = adapter viewModel.picInfo.observe(viewLifecycleOwner, Observer { // 直接提交接口返回的数据集即可 adapter.submitList(it) // 数据加载完成后跳转到中间位置,避免一开始滑到左边界 if (it.isNotEmpty()) { viewPager.setCurrentItem(Int.MAX_VALUE / 2, false) } })
额外优化点
如果需要实现自动轮播,只需要额外加一个定时任务每隔固定时间调用viewPager.setCurrentItem(viewPager.currentItem + 1, true)即可,不需要修改现有适配器逻辑。
内容的提问来源于stack exchange,提问作者OEThe11
相关产品推荐
相关产品推荐

