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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 08:27:45