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

Android Kotlin ListAdapter搭配DiffUtil onResume全列表重绘问题咨询

问题1:DiffUtil回调实现是否正确

你当前的DiffUtil实现是可用的,但存在可优化点:

  • areItemsTheSame 逻辑正确,用唯一id判断两个条目是否是同一个对象,符合要求。
  • areContentsTheSame 手动对比所有属性的逻辑是对的,因为你用到的嵌套类Qqq、Rr都是data class,默认实现了全属性的equals对比,所以可以直接简化为return oldItem == newItem,避免后续新增属性时漏改对比逻辑的问题。

问题2:列表全量重绘的原因和优化方案

核心问题根源

你当前每次收到接口数据都会重新创建Adapter实例、重新给RecyclerView设置Adapter,这直接导致RecyclerView完全重置,DiffUtil的增量更新能力完全失效,自然每次都会全量重绘所有条目。

优化方案


1. 仅初始化一次Adapter

将Adapter的初始化逻辑从数据回调中移到onCreate中,仅执行一次,后续收到新数据仅调用submitList提交数据即可,同时满足你校验abc不为空的要求:
修改后的ListActivity.kt核心代码:

class ListActivity : BaseActivity() {
    lateinit var binding: ListActivityLayoutBinding
    private val viewModel: ListViewModel by inject()
    // 改为不可空,初始化后不再修改
    private lateinit var listAdapter: ListAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ListActivityLayoutBinding.inflate(layoutInflater)
        setContentView(binding.root)
        // 提前设置RecyclerView的布局管理器,你原有代码缺漏可自行补上
        binding.recyclerView.layoutManager = LinearLayoutManager(this)
        setupViewModel()
    }

    private fun setupViewModel() {
        viewModel.liveData.observe(this) { list ->
            // 仅当Adapter未初始化时,才执行初始化逻辑
            if (!::listAdapter.isInitialized) {
                viewModel.abc?.let { abc ->
                    listAdapter = ListAdapter(abc, object : Listener<XYZ> {
                        override fun selectedItem(item: XYZ) {
                            // 保留原有逻辑
                        }
                    })
                    binding.recyclerView.adapter = listAdapter
                } ?: run {
                    Log.e("Error", "Error for fetching data")
                    return@observe
                }
            }
            // 直接提交数据,DiffUtil会自动计算增量更新
            listAdapter.submitList(list)
        }
    }

    override fun onResume() {
        super.onResume()
        viewModel.fetchData()
    }
}

2. ViewModel层增加数据变更校验,无变更不通知UI

每次从接口拿到新数据后,先和上一次缓存的数据做对比,只有内容发生变更时才给LiveData post新值,避免无意义的UI刷新:
修改后的ListViewModel.kt核心代码:

class ListViewModel : BaseViewModel() {
    var abc: Abc? = null
    // 缓存上一次的有效列表数据
    private var lastXyz: List<XYZ>? = null
    var liveData: MutableLiveData<List<XYZ>> = MutableLiveData()

    fun fetchData() {
        viewModelScope.launch {
            val firstAsync = async {
                if (abc == null) {
                    abc = getAbc() // First retrofit call
                }
            }
            val secondAsync = async {
                getXYZ() // Second retrofit call,直接返回结果不赋值给全局变量
            }
            firstAsync.await()
            val newXyz = secondAsync.await()
            // 只有数据发生变更时才通知UI
            if (newXyz != lastXyz) {
                lastXyz = newXyz
                liveData.postValue(newXyz)
            }
        }
    }
}

3. 可选优化:DiffUtil简化

直接利用data class的默认equals能力简化对比逻辑,修改ListAdapter.kt中的对比器:

private val LIST_COMPARATOR = object : DiffUtil.ItemCallback<XYZ>() {
    override fun areItemsTheSame(oldItem: XYZ, newItem: XYZ): Boolean {
        return oldItem.id == newItem.id
    }

    override fun areContentsTheSame(oldItem: XYZ, newItem: XYZ): Boolean {
        // data class默认全属性对比,无需手动逐个写
        return oldItem == newItem
    }
}

效果说明

修改完成后:

  • 首次加载数据时会全量绘制所有条目,符合你的要求
  • 后续onResume触发接口请求时,如果接口返回数据和上一次完全一致,ViewModel不会通知UI,列表无任何刷新
  • 如果数据有变更,DiffUtil会自动计算新增、删除、修改的条目,仅更新发生变化的部分,不会全量重绘

内容的提问来源于stack exchange,提问作者Compose Learner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 21:54:03