RecyclerView.Adapter刷新优化咨询:是否有更简便的更新方案?
你提供的自定义RecyclerView.Adapter代码如下:
public abstract class MyCustomRecyclerViewAdapter extends RecyclerView.Adapter { private Context context; private List<?> data = new ArrayList<>(); public DataBindingRecyclerViewAdapter(Context context, List<?> data) { this.context = context; if (data != null) { this.data = data; } } public void updateData(List data) { this.data.clear(); if (data != null) { this.data.addAll(data); } this.notifyDataSetChanged(); } }
当前你的updateData()方法需要按「清空数据→添加新数据→调用notifyDataSetChanged()」的流程才能刷新,单独调用notifyDataSetChanged()无法生效,想要更简便的刷新方案。
1. 使用DiffUtil实现高效局部刷新(推荐)
notifyDataSetChanged()是全量刷新,会触发整个RecyclerView重绘,效率较低,而且需要手动维护数据的清空和添加。Google提供的DiffUtil可以自动计算新旧数据的差异,只刷新变化的部分,代码也更简洁。
改造你的updateData()方法:
public void updateData(List<?> newData) { // 计算新旧数据的差异 DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallback(this.data, newData)); // 更新数据源 this.data.clear(); if (newData != null) { this.data.addAll(newData); } // 分发差异更新 diffResult.dispatchUpdatesTo(this); } // 定义DiffCallback类 private static class DiffCallback extends DiffUtil.Callback { private final List<?> oldData; private final List<?> newData; public DiffCallback(List<?> oldData, List<?> newData) { this.oldData = oldData; this.newData = newData; } @Override public int getOldListSize() { return oldData.size(); } @Override public int getNewListSize() { return newData != null ? newData.size() : 0; } @Override public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { // 这里需要根据你的数据类型判断两个item是否是同一个对象(比如用id比较) // 示例:如果你的数据是实体类,假设实体类有getId()方法 // Object oldItem = oldData.get(oldItemPosition); // Object newItem = newData.get(newItemPosition); // return oldItem instanceof YourEntity && newItem instanceof YourEntity // && ((YourEntity) oldItem).getId() == ((YourEntity) newItem).getId(); // 如果是简单数据类型(比如String/Integer),可以直接equals: return oldData.get(oldItemPosition).equals(newData.get(newItemPosition)); } @Override public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { // 判断两个item的内容是否一致,一致则不需要刷新 return oldData.get(oldItemPosition).equals(newData.get(newItemPosition)); } }
这样不仅不需要手动纠结刷新时机,还能实现局部刷新,性能更好。如果你的数据是复杂实体类,记得在areItemsTheSame和areContentsTheSame里做针对性的判断。
2. 优化现有方案,支持直接更新数据后刷新
你说单独调用notifyDataSetChanged()不生效,大概率是因为你修改了外部的数据源列表,但Adapter持有的data引用和外部列表是同一个,修改后没有通知;或者直接替换了data的引用但没刷新。
可以修改Adapter,支持两种刷新方式:
// 直接替换数据源引用并刷新 public void setData(List<?> newData) { this.data = newData != null ? newData : new ArrayList<>(); notifyDataSetChanged(); } // 允许外部修改现有数据源后,手动触发刷新 public void refresh() { notifyDataSetChanged(); }
比如你在外部直接给data添加/删除元素后,调用adapter.refresh()就能触发刷新,不需要再走清空添加的流程。
3. 使用精细化的notify方法
如果只是做单个数据的添加、删除、修改,直接用对应的notify方法,比全量刷新更高效:
- 添加单个数据:
notifyItemInserted(position) - 删除单个数据:
notifyItemRemoved(position) - 修改单个数据:
notifyItemChanged(position) - 批量操作:
notifyItemRangeInserted(startPosition, count)等
比如添加一条数据到列表末尾的示例:
public void addItem(Object item) { data.add(item); notifyItemInserted(data.size() - 1); }
这样只会刷新新增的那一行,性能比notifyDataSetChanged()好很多。
内容的提问来源于stack exchange,提问作者user8542613

