RecyclerView

DiffUtil

最大的用处就是在RecyclerView刷新时,不再无脑mAdapter.notifyDataSetChanged()

它会自动计算新老数据集的差异,并根据差异情况,自动调用以下四个方法

adapter.notifyItemRangeInserted(position, count);
adapter.notifyItemRangeRemoved(position, count);
adapter.notifyItemMoved(fromPosition, toPosition);
adapter.notifyItemRangeChanged(position, count, payload);

1. 基本用法

实现DiffUtil.Callback,然后调用DiffUtil.calculateDiff()方法,计算出新老数据集转化的最小更新集,就是DiffUtil.DiffResult对象。 然后调用DiffUtil.DiffResult.dispatchUpdatesTo(RecyclerView.Adapter adapter)方法,传入RecyclerView的Adapter,最后,更新数据集即可。

//示例步骤:
public class StudentDiffCallback extends DiffUtil.Callback {
    private List<Student> oldList;
    private List<Student> list;

    public StudentDiffCallback(List<Student> oldList, List<Student> list) {
        this.oldList = oldList;
        this.list = list;
    }

    @Override
    public int getOldListSize() {
        return oldList.size();
    }

    @Override
    public int getNewListSize() {
        return list.size();
    }

    @Override
    public boolean areItemsTheSame(int i, int i1) {
        return oldList.get(i).id == list.get(i1).id;
    }

    @Override
    public boolean areContentsTheSame(int i, int i1) {
        Student oldStudent = oldList.get(i);
        Student student = list.get(i1);
        return oldStudent.id == student.id &&
                oldStudent.age == student.age &&
                oldStudent.name.equals(student.name);
    }
}

        //使用步骤
        DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new StudentDiffCallback(adapter.getDatas(), list));
        diffResult.dispatchUpdatesTo(adapter);
        adapter.updateList(list);

2.高级用法

高级用法只涉及到两个方法, 我们需要分别实现DiffUtil.Callback.getChangePayload(int oldItemPosition, int newItemPosition), 返回的Object就是表示Item改变了哪些内容。

再配合RecyclerView.Adapter.onBindViewHolder(VH holder, int position, List payloads)方法, 完成定向刷新。

payloads 参数 是一个从(notifyItemChanged(int, Object)或notifyItemRangeChanged(int, int, Object))里得到的合并list。 如果payloads list 不为空,那么当前绑定了旧数据的ViewHolder 和Adapter, 可以使用 payload的数据进行一次 高效的部分更新。

如果payload 是空的,Adapter必须进行一次完整绑定(调用两参方法)。

注意1: 没有重写DiffUtil.Callback.getChangePayload(int oldItemPosition, int newItemPosition) 时,更新列表时,有数据更新的item会闪白光,如果不想要白光的话,重写这个方法即可。

注意2:如果数据量多比较耗时,计算新老数据集差异的逻辑可以写在子线程,然后再主线程更新,减少主线程开销。

Last updated