注册

DiffUtil 让 RecyclerView 更好用

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的支持。

直接看官方文档吧。

代码下载:https://github.com/CymChad/BaseRecyclerViewAdapterHelper/archive/refs/heads/master.zip

0 个评论

要回复文章请先登录注册