Kotlin中ListAdapter调用submitList后RecyclerView滚动位置重置如何解决
问题解决方案
核心原因
闪烁和滚动位置重置问题的根源是DiffUtil.ItemCallback的实现逻辑错误,导致DiffUtil无法正确识别新旧列表的相同项,误判为整个列表发生了全量替换,触发了全局重绘。
修复步骤
1. 修正DiffCallback实现
将areItemsTheSame的引用相等判断替换为唯一ID判断,同时areContentsTheSame需要覆盖所有UI相关字段的校验:
companion object DiffCallback : DiffUtil.ItemCallback<Template>() { override fun areItemsTheSame(oldItem: Template, newItem: Template): Boolean { // 用唯一ID判断是否是同一个条目,而非引用相等 return oldItem.id_template == newItem.id_template } override fun areContentsTheSame(oldItem: Template, newItem: Template): Boolean { // 所有在列表项中展示的字段都需要加入对比,确保仅内容变化时才触发条目刷新 return oldItem.id_template == newItem.id_template && oldItem.title == newItem.title && oldItem.createTime == newItem.createTime // 按需补充其他业务使用的展示字段 } }
2. 可选:手动保留滚动位置
如果修正Diff后仍存在滚动位置偏移,可以在BindingAdapter中利用submitList的完成回调手动恢复滚动位置:
@BindingAdapter("listTemplate", "hirarki") fun bindListTemplate(recyclerView: RecyclerView, data: List<Template>?, hirarki: Int) { val adapter = recyclerView.adapter as TemplateChiefAdapter val layoutManager = recyclerView.layoutManager as LinearLayoutManager // 提交前保存当前滚动位置 val firstVisiblePos = layoutManager.findFirstVisibleItemPosition() val topOffset = layoutManager.getChildAt(0)?.top ?: 0 // 提交列表,Diff计算完成后恢复位置 adapter.submitList(data) { layoutManager.scrollToPositionWithOffset(firstVisiblePos, topOffset) } }
3. 辅助优化减少闪烁
- 给RecyclerView开启固定大小优化:在XML中给RecyclerView添加
app:hasFixedSize="true",如果列表的整体尺寸不会随内容变化,这个配置可以避免不必要的重绘。 - 确保列表项布局设置了明确的高度,避免RecyclerView重新测量条目导致的位置偏移。
- 每次调用submitList时传入新的列表副本,不要复用旧列表对象修改后直接提交,否则ListAdapter会跳过Diff计算。
内容的提问来源于stack exchange,提问作者Space Colony
相关产品推荐
相关产品推荐

