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