DiffUtil 让 RecyclerView 更好用

3,899 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

DiffUtil 让 RecyclerView 更好用

前几天在写局部刷新RecyclerView时,评论区有掘友提到了DiffUtil,说实话,确实没有在项目中用到过,查了资料,DiffUtil帮我们做了很多刷新很多工作,真香。

DiffUtil是什么

DiffUtil 是来自recycleview-v7下的工具类,Diff 直接翻译过来是 差异、对比,所以这个工具类主要帮助我们对比两个数据集,寻找出最小的变化量。那么它和RecyclerView有什么关系呢,实际上我们只要把新旧数据集给到DiffUtil,那么它就会自动帮我们对比数据,并且刷新适配器,而不用我们判断,是增加了删除了等等,DiffUtil对比之后自动帮我们搞定,这就是它非常好用的地方了

常规的适配器

我们先用RecyclerView写一个常规的列表。
它拥有刷新item和item局部刷新的功能。
代码如下:

MainActivity: 主界面

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;

    private List<PersonInfo> mDatas;
    private PersonAdapter personAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recyclerView);
        initData();
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        personAdapter = new PersonAdapter(this, mDatas);
        recyclerView.setAdapter(personAdapter);
    }

    private void initData() {
        mDatas = new ArrayList<>();
        mDatas.add(new PersonInfo(1, "姓名1"));
        mDatas.add(new PersonInfo(2, "姓名2"));
        mDatas.add(new PersonInfo(3, "姓名3"));
        mDatas.add(new PersonInfo(4, "姓名4"));
        mDatas.add(new PersonInfo(5, "姓名5"));
        mDatas.add(new PersonInfo(6, "姓名6"));
        mDatas.add(new PersonInfo(7, "姓名7"));
        mDatas.add(new PersonInfo(8, "姓名8"));
        mDatas.add(new PersonInfo(9, "姓名9"));
        mDatas.add(new PersonInfo(10, "姓名10"));
        mDatas.add(new PersonInfo(11, "姓名11"));
    }

    public void ADD(View view) {
        int position = mDatas.size();
        List<PersonInfo> tempData = new ArrayList<>();

        tempData.add(new PersonInfo(12, "姓名12"));
        tempData.add(new PersonInfo(13, "姓名13"));
        tempData.add(new PersonInfo(14, "姓名114"));

        mDatas.addAll(tempData);
        personAdapter.notifyItemRangeInserted(position, tempData.size());
    }

    public void DELETE(View view) {
        mDatas.remove(1);
        personAdapter.notifyItemRemoved(1);
    }

    public void UPDATE(View view) {
        mDatas.get(1).setName("姓名:我被更新了");
        personAdapter.notifyItemChanged(1);
    }

    public void UPDATE2(View view) {
        mDatas.get(1).setName("姓名:我被更新了");

        Bundle payload = new Bundle();
        payload.putString("KEY_NAME", mDatas.get(1).getName());
        personAdapter.notifyItemChanged(1, payload);
    }
}

PersonAdapter: 适配器

public class PersonAdapter extends RecyclerView.Adapter<PersonAdapter.DiffVH> {
    private List<PersonInfo> mDatas;
    private LayoutInflater mInflater;

    public PersonAdapter(Context context, List<PersonInfo> mDatas) {
        this.mDatas = mDatas;
        mInflater = LayoutInflater.from(context);
    }

    public void setDatas(List<PersonInfo> mDatas) {
        this.mDatas = mDatas;
    }

    @Override
    public DiffVH onCreateViewHolder(ViewGroup parent, int viewType) {
        return new DiffVH(mInflater.inflate(R.layout.item_person, parent, false));
    }

    @Override
    public void onBindViewHolder(final DiffVH holder, final int position) {
        PersonInfo personInfo = mDatas.get(position);
        holder.tv_index.setText(String.valueOf(personInfo.getIndex()));
        holder.tv_name.setText(String.valueOf(personInfo.getName()));
    }

    @Override
    public void onBindViewHolder(DiffVH holder, int position, List<Object> payloads) {
        if (payloads.isEmpty()) {
            onBindViewHolder(holder, position);
        } else {
            Bundle payload = (Bundle) payloads.get(0);
            PersonInfo bean = mDatas.get(position);
            for (String key : payload.keySet()) {
                switch (key) {
                    case "KEY_INDEX":
                        holder.tv_index.setText(String.valueOf(bean.getIndex()));
                        break;
                    case "KEY_NAME":
                        holder.tv_name.setText(String.valueOf(bean.getName()));
                        break;
                    default:
                        break;
                }
            }
        }
    }

    @Override
    public int getItemCount() {
        return mDatas != null ? mDatas.size() : 0;
    }

    class DiffVH extends RecyclerView.ViewHolder {
        TextView tv_index;
        TextView tv_name;

        public DiffVH(View view) {
            super(view);
            tv_index = view.findViewById(R.id.tv_index);
            tv_name = view.findViewById(R.id.tv_name);
        }
    }
}

PersonInfo: 实体类

public class PersonInfo {
    private int index;
    private String name;

    public PersonInfo(int index, String name) {
        this.index = index;
        this.name = name;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

activity_main

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="增加"
            android:onClick="ADD"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="删除"
            android:onClick="DELETE"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="修改"
            android:onClick="UPDATE"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="局部更新"
            android:onClick="UPDATE2"/>

    </LinearLayout>


    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


</LinearLayout>

item_person

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:background="@color/purple_200"
        android:orientation="horizontal"
        android:padding="5dp">

        <TextView
            android:id="@+id/tv_index"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true" />

        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toEndOf="@+id/tv_index" />

    </RelativeLayout>


</LinearLayout>

引入DiffUtil

我们创建一个DiffUtil类,在需要更新的时候,用DiffUtil中的方法去代替原本的刷新方法。

用新增举例,这样就能达到更新的目的

public void ADD(View view) {
        List<PersonInfo> newData = new ArrayList<>();
        newData.addAll(mDatas);
        newData.add(new PersonInfo(12, "姓名12"));
        newData.add(new PersonInfo(13, "姓名13"));
        newData.add(new PersonInfo(14, "姓名114"));

        DiffUtil.calculateDiff(new DiffUtilCallBack(newData,mDatas), true).dispatchUpdatesTo(personAdapter);
        mDatas = newData;
        personAdapter.setDatas(mDatas);
    }
   

DiffUtilCallBack

public class DiffUtilCallBack extends DiffUtil.Callback {
    private List<PersonInfo> newlist;
    private List<PersonInfo> oldlist;

    public DiffUtilCallBack(List<PersonInfo> newlist, List<PersonInfo> oldlist) {
        this.newlist = newlist;
        this.oldlist = oldlist;
    }

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

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

    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        //判断是否是同一个item,可以在这里处理   判断是否是相同item的逻辑,比如id之类的
        return newlist.get(newItemPosition).getIndex() == oldlist.get(oldItemPosition).getIndex();
    }

    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        //判断数据是否发生改变,这个  方法会在上面的方法返回true时调用,  因为虽然item是同一个,但有可能item的数据发生了改变
        return newlist.get(newItemPosition).getName().equals(oldlist.get(oldItemPosition).getName());
    }
}

万能适配器中的DiffUtil

配合RecyclerView,我一直在使用万能适配器(BaseRecyclerViewAdapterHelper),如果你也习惯了使用万能适配器,在它的3.0方法中引入了对DiffUtil的支持。

直接看官方文档吧。

点我直达