DiffUtil 未调用 onBindView 或不刷新可见 ViewHolder 怎么办
DiffUtil更新后需滚动才显示新值的问题排查
当前实现场景
要做固定30条目的列表,每分钟刷新数据,为了避免滥用notifyDataSetChanged引入DiffUtil差量更新机制,目前实现如下:
自定义DiffUtil.Callback
public class DifUtil extends DiffUtil.Callback { private final List<Asset> oldList, newList; public DifUtil(List<Asset> newList, List<Asset> oldList) { this.oldList = oldList; this.newList = newList; } @Override public int getOldListSize() { return oldList.size(); } @Override public int getNewListSize() { return newList.size(); } @Override public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { return oldList.get(oldItemPosition).getId().equals(newList.get(newItemPosition).getId()); } @Override public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { return oldList.get(oldItemPosition).equals(newList.get(newItemPosition)); } @Nullable @Override public Object getChangePayload(int oldItemPosition, int newItemPosition) { return super.getChangePayload(oldItemPosition, newItemPosition); } }
Adapter内的更新方法
public void updateList(List<Asset> newList) { DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DifUtil(newList, this.assetList)); this.assetList.clear(); this.assetList.addAll(newList); diffResult.dispatchUpdatesTo(this); }
其他已实现逻辑
- 重写了带payload参数的
onBindViewHolder,方法签名为:
onBindViewHolder(@NonNull AssetsAdapter.ViewHolder holder, int position, @NonNull List<Object> payloads)
- 调用更新方法仅需传入最新的列表:
adapter.updateList(newAssetList);
异常表现
Adapter持有的列表数据已经完成更新,但只有滚动列表触发视图回收时,界面上才能看到最新值,达不到和notifyItemChanged()一致的即时刷新效果。
核心排查点与修复方案
按照以下顺序排查,即可解决即时刷新的问题:
- 优先检查
Asset类的equals方法实现
你在areContentsTheSame中直接通过两个Asset对象的equals方法判断内容是否一致。如果没有手动重写Asset的equals方法,默认会比较对象的内存地址,永远不会判断内容相等;如果重写了但遗漏了需要实时更新的业务字段(比如每分钟变动的数值类字段),DiffUtil会误判条目内容无变化,不触发对应位置的notifyItemChanged通知。
这种场景下,你虽然已经把新数据更新到了Adapter的列表中,但ViewHolder因为没有收到更新通知,仍绑定着旧数据。只有滚动时ViewHolder被回收重新执行绑定逻辑,才会读取到新列表的数据,和你遇到的现象完全匹配。 - 确认差量计算的执行顺序
你目前的调用顺序(先对比新旧列表计算差量→再替换Adapter持有的列表数据→最后分发更新通知)是正确的。千万不要调换为“先清空Adapter旧列表、添加新数据,再计算Diff差量”,这种顺序会导致DiffUtil拿内容完全一致的两个列表做对比,计算结果为空,不会触发任何界面更新通知。 - 检查三参数
onBindViewHolder的绑定逻辑
如果你重写了带payload参数的onBindViewHolder,必须保证当payloads为空时,执行和两参数onBindViewHolder完全一致的绑定逻辑,将item数据正确设置到ViewHolder的控件上。不要把全量绑定逻辑只写在payload非空的分支里,否则DiffUtil分发不带payload的条目更新时,会因为没执行绑定逻辑导致界面不刷新。 - 可选优化
你当前的列表是固定30条,不存在条目增删、移动的场景,可以在调用calculateDiff时显式关闭移动检测,减少差量计算的耗时:// 第二个参数传false,关闭条目移动检测 DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DifUtil(newList, this.assetList), false);
内容的提问来源于stack exchange,提问作者Undefined function
相关产品推荐
相关产品推荐

