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
相关产品推荐
相关产品推荐

