问题描述
我现在正在创建一个 DiffUtil
类来仅更新 RecyclerView
中已更改的项目。
我看过其他几个示例代码。
在比较两个对象时,他们比较了Model(Data)
中areItemsTheSame()
类中定义的id等唯一值。
不过我觉得给List赋值一个id
或者unique value
比较困难,或者代码很乱。
我是否必须像这样定义和比较 id
?
我真的需要在分隔每个对象的 Model class
中定义一个唯一的 Id 变量吗?
或者我不应该只使用 equals()
吗?
使用这个不就是比较对象的地址,还比较对象的内容吗?
作为附加问题
DiffUtil.CallBack
和 DiffUtil.ItemCallBack
有什么区别?
这是我的代码。
RoutineModel.java
public class RoutineModel {
private ArrayList<RoutineDetailModel> routineDetailModels;
private String routine;
public RoutineModel(ArrayList<RoutineDetailModel> items,String routine) {
this.routine = routine;
this.routineDetailModels = items;
}
public ArrayList<RoutineDetailModel> getDetailItemList() {
return routineDetailModels;
}
public int getDetailItemSize() {
return routineDetailModels.size();
}
public String getRoutine() {
return routine;
}
public void setRoutine(String routine) {
this.routine = routine;
}
}
RoutineDiffUtil.java
public class RoutineDiffUtil extends DiffUtil.Callback {
private final List<RoutineModel> oldRoutineList;
private final List<RoutineModel> newRoutineList;
public RoutineDiffUtil(ArrayList<RoutineModel> oldRoutineList,ArrayList<RoutineModel> newRoutineList) {
this.oldRoutineList = oldRoutineList;
this.newRoutineList = newRoutineList;
}
@Override
public int getOldListSize() {
return oldRoutineList.size();
}
@Override
public int getNewListSize() {
return newRoutineList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition,int newItemPosition) {
return oldRoutineList.equals(newRoutineList);
}
@Override
public boolean areContentsTheSame(int oldItemPosition,int newItemPosition) {
return oldRoutineList.equals(newRoutineList);
}
}
解决方法
您弄错了 areItemsTheSame()
和 areContentsTheSame()
回调的含义。如您所见,其中有 oldItemPosition
和 newItemPosition
参数。您应该使用它们来比较特定项目 - 而不是列表本身。
在areItemsTheSame()
中,您必须检查旧列表中“旧”位置的模型是否等于新列表中“新”位置的模型.这就是 DiffUtil 知道它是否必须制作重新排序动画的方式。
areContentsTheSame()
时,才会为两个项目调用 true
。在这里,您必须检查“旧”和“新”模型的视觉表示是否相同。这就是 DiffUtil 知道它是否必须制作“项目更改”动画的方式。
要比较两个模型,您必须覆盖 equals()
和 hashCode()
。在那里您可以指定将两个模型视为相同的条件。例如,如果它们具有相同的 routine
。我不知道你的任务的上下文,所以我不能确切地告诉你如何实现它们,但通常你只是比较所有领域。可能添加一个 id
字段也是一个好主意。然后,如果模型具有相同的 id
,则您可以将模型视为“相等”。而在 hashCode()
中,您只需返回 Objects.hash(id)
。
现在,谈谈您关于 ItemCallback
的问题。正式地,这里是文档的解释:
DiffUtil.Callback 有两个作用 - 列表索引和项目差异。 ItemCallback 只处理其中的第二个,这允许将索引到数组或列表的代码与表示层和内容特定的差异代码分开。
实际上,ItemCallback
只是需要实现的方法较少,并且与 AsyncListDiffer 一起使用。这只是因为缺少的方法已经在 AsyncListDiffer 中实现了。
您必须覆盖模型类的等号和哈希码。
例程模型:
class RoutineModel {
private ArrayList<RoutineDetailModel> routineDetailModels;
private String routine;
public RoutineModel(ArrayList<RoutineDetailModel> items,String routine) {
this.routine = routine;
this.routineDetailModels = items;
}
public ArrayList<RoutineDetailModel> getDetailItemList() {
return routineDetailModels;
}
public int getDetailItemSize() {
return routineDetailModels.size();
}
public String getRoutine() {
return routine;
}
public void setRoutine(String routine) {
this.routine = routine;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RoutineModel that = (RoutineModel) o;
return Objects.equals(routineDetailModels,that.routineDetailModels) &&
Objects.equals(routine,that.routine);
}
@Override
public int hashCode() {
return Objects.hash(routineDetailModels,routine);
}
}
RoutineDiffUtil:
public class RoutineDiffUtil extends DiffUtil.Callback {
private final List<RoutineModel> oldRoutineList;
private final List<RoutineModel> newRoutineList;
public RoutineDiffUtil(ArrayList<RoutineModel> oldRoutineList,ArrayList<RoutineModel> newRoutineList) {
this.oldRoutineList = oldRoutineList;
this.newRoutineList = newRoutineList;
}
@Override
public int getOldListSize() {
return oldRoutineList.size();
}
@Override
public int getNewListSize() {
return newRoutineList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition,int newItemPosition) {
return oldRoutineList.get(oldItemPosition).getRoutine().equals(newRoutineList.get(newItemPosition).getRoutine());
}
@Override
public boolean areContentsTheSame(int oldItemPosition,int newItemPosition) {
return oldRoutineList.get(oldItemPosition).equals(newRoutineList.get(newItemPosition));
}
}
并且不要忘记覆盖RoutineDetailModel的equals和hashcode。