Android RecyclerView向上滚动时适配器重复更新数据问题求助
解决RecyclerView向上滚动时适配器重复更新数据的问题
我帮你梳理了代码里的问题,这几个点是导致RecyclerView向上滚动时重复更新数据的关键:
问题1:getItemViewType返回position完全错误
你在适配器里定义了ADS和LANGUAGES两种item类型,但现在每个位置的viewType都等于position,这会让RecyclerView认为每个item的布局都是独一无二的,完全无法复用ViewHolder。每次滚动时都会重新创建ViewHolder并调用onBindViewHolder,视觉上就表现为重复更新数据。
问题2:getItemId返回position不合理
RecyclerView依赖itemId来识别同一个数据项,返回position的话,当列表滚动或数据变化时,同一个数据的position改变,id也会跟着变,导致RecyclerView无法正确复用已有的item视图,触发不必要的刷新。
问题3:DiffUtil的优化作用被抵消
虽然你实现了DiffUtil的回调,但因为viewType和itemId的错误设置,DiffUtil的高效更新机制根本发挥不了作用,反而因为频繁创建ViewHolder导致性能问题。
修复后的代码实现
1. 修正LanguageAdapter核心逻辑
class LanguageAdapter( private val context: Context, private val mViewModel: LanguageListViewModel, private val onClickListener: OnItemClickListener<String> ) : MultiViewModelBaseAdapter<LanguageSupportModel, ViewDataBinding>(diffCallback) { companion object { private val ADS = 1 private val LANGUAGES = 2 val diffCallback = object : DiffUtil.ItemCallback<LanguageSupportModel>() { override fun areItemsTheSame( oldItem: LanguageSupportModel, newItem: LanguageSupportModel ): Boolean = oldItem.dataId == newItem.dataId override fun areContentsTheSame( oldItem: LanguageSupportModel, newItem: LanguageSupportModel ): Boolean = oldItem == newItem } } // 修复:用数据的唯一标识作为itemId,确保RecyclerView能识别同一数据项 override fun getItemId(position: Int): Long { return getItem(position).dataId.toLong() // 根据dataId的实际类型调整转换方式 } // 修复:根据item的实际类型返回对应的viewType,而不是position override fun getItemViewType(position: Int): Int { val item = getItem(position) // 这里替换成你判断广告/语言项的实际逻辑,比如item的某个标记字段 return if (item.isAd) ADS else LANGUAGES } // 适配不同viewType加载对应的布局 override fun createBinding(viewType: Int, inflater: LayoutInflater, parent: ViewGroup): ViewDataBinding { val layoutId = if (viewType == ADS) R.layout.ad_view else R.layout.language_view return DataBindingUtil.inflate(inflater, layoutId, parent, false) } override fun bind(binding: ViewDataBinding, item: LanguageSupportModel, position: Int) { when(binding) { is LanguageViewDataBinding -> { binding.language = item // 在这里设置点击事件等逻辑 binding.root.setOnClickListener { onClickListener.onItemClick(item.languageCode) } } is AdViewDataBinding -> { // 如果有广告项,在这里处理广告的绑定逻辑 } } binding.executePendingBindings() } override fun onDataChanged(values: Boolean) {} }
2. 优化Fragment中的适配器初始化逻辑
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val languageAdapter = LanguageAdapter(requireContext(), mViewModel, this) reyclerview.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) reyclerview.adapter = languageAdapter // 先设置适配器再提交数据 // 静态数据提交一次即可;如果是动态数据,建议观察ViewModel的LiveData来触发submitList languageAdapter.submitList(LanguageArray.arrayValues()) }
额外注意事项
- 确保
LanguageSupportModel是数据类,这样oldItem == newItem才能正确比较对象内容 - 如果你的列表里没有广告类型,可以直接删除
ADS相关逻辑,返回固定的viewType(比如return 1),进一步提升复用效率 - 不要在
onBindViewHolder中执行耗时操作(比如网络请求、复杂计算),避免滚动卡顿
内容的提问来源于stack exchange,提问作者Ahsan Ali
相关产品推荐
相关产品推荐

