发布于 1年前

Android使用DiffUtil智能地更新RecyclerView

DiffUtil是一个工具类,它会找出两个列表的差异,输出一个更新列表,并且可以用来通知RecyclerView的Adapter更新。

使用

DiffUtil会使用DiffUtil.Callback来计算两个列表的差异,其中DiffUtil.Callback是一个抽象类,需要我们实现它,它包含了四个抽象方法和一个非抽象方法:

  • getOldListSize():获取旧列表的大小

  • getNewListSize():获取新列表的大小

  • areItemsTheSame(int oldItemPosition, int newItemPosition):判断新旧列表两个位置的项目是否相同

  • areContentsTheSame(int oldItemPosition, int newItemPosition):判断新旧列表两个位置的项目数据是否相同,DiffUtil只会在areItemsTheSame()返回true时才调用此方法。

  • getChangePayload(int oldItemPosition, int newItemPosition):如果areItemTheSame()返回true,而areContentsTheSame()返回false,则DiffUtil会调用此方法来获取更改后的负载

示例

模型类:Employee

public class Employee {
    public int id;
    public String name;
    public String role;
}

实现DiffUtil.Callback

public class EmployeeDiffCallback extends DiffUtil.Callback {

    private final List<Employee> mOldEmployeeList;
    private final List<Employee> mNewEmployeeList;

    public EmployeeDiffCallback(List<Employee> oldEmployeeList, List<Employee> newEmployeeList) {
        this.mOldEmployeeList = oldEmployeeList;
        this.mNewEmployeeList = newEmployeeList;
    }

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

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

    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        return mOldEmployeeList.get(oldItemPosition).getId() == mNewEmployeeList.get(
                newItemPosition).getId();
    }

    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        final Employee oldEmployee = mOldEmployeeList.get(oldItemPosition);
        final Employee newEmployee = mNewEmployeeList.get(newItemPosition);

        return oldEmployee.getName().equals(newEmployee.getName());
    }

    @Nullable
    @Override
    public Object getChangePayload(int oldItemPosition, int newItemPosition) {
        // 如果要使用ItemAnimator,可以在实现此方法
        return super.getChangePayload(oldItemPosition, newItemPosition);
    }
}

在RecyclerViewAdapter处理列表数据的更新

public class CustomRecyclerViewAdapter extends RecyclerView.Adapter<CustomRecyclerViewAdapter.ViewHolder> {

  ...
       public void updateEmployeeListItems(List<Employee> employees) {
        final EmployeeDiffCallback diffCallback = new EmployeeDiffCallback(this.mEmployees, employees);
        final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);

        this.mEmployees.clear();
        this.mEmployees.addAll(employees);
        diffResult.dispatchUpdatesTo(this); //分发更新到RecyclerView Adapter
    }
}

DiffResult为计算差异后返回的结果,获取差异结果后,调用dispatchUpdatesTo(RecyclerView.Adapter)来分发更新的列表。

DiffUtil调用了RecyclerView.Adapter的以下方法来通知Adapter来更新数据集:

  • notifyItemMoved()
  • notifyItemRangeChanged()
  • notifyItemRangeInserted()
  • notifyItemRangeRemoved()
©2020 edoou.com   京ICP备16001874号-3