都不是本篇的内容,但是,需要注意到的一个地方是Adapter的定义:
1 class MyAdapter extends RecyclerView.Adapter {
2 private ArrayList<Student> data;
3
4 ArrayList<Student> getData() {
5 return data;
6 }
7
8 void setData(ArrayList<Student> data) {
9 this.data = new ArrayList<>(data);
10 }
11
12 // 省略部分代码
13 ......
14 }
这里的setData方法并不是直接将ArrayList的引用保存,而是重新的建立一个ArrayList,先记着,后面会解释为什么要这样做。
DiffUtil的使用方法:
当鼠标按下时,修改ArrayList的内容:
1 public void change(View view) {
2 students.set(1, new Student("Fndroid", 2));
3 students.add(new Student("Jason", 8));
4 Student s2 = students.get(2);
5 students.remove(2);
6 students.add(s2);
7
8 ArrayList<Student> old_students = adapter.getData();
9 DiffUtil.DiffResult result = DiffUtil.calculateDiff(new MyCallback(old_students, students), true);
10 adapter.setData(students);
11 result.dispatchUpdatesTo(adapter);
12 }
2-6行是对集合进行修改,第8行先获取到adapter中的集合为旧的数据。
重点看第9行调用DiffUtil.calculateDiff方法来计算集合的差别,这里要传入一个CallBack接口的实现类(用于指定计算的规则)并且把新旧数据都传递给这个接口的实现类,最后还有一个boolean类型的参数,这个参数指定是否需要进行Move的检测,如果不需要,如果有Item移动了,会被认为是先remove,然后insert。这里指定为true,所以就有了动图显示的移动效果。
第10行重新将新的数据设置给Adapter。
第11行调用第9行得到的DiffResult对象的dispatchUpdatesTo方法通知RecyclerView刷新对应发生变化的Item。
这里回到上面说的setData方法,因为我们在这里要区分两个集合,如果在setData方法中直接保存引用,那么在2-6行的修改就直接修改了Adapter中的集合了(Java知识)。
如果设置不检查Item的移动,效果如下:
接着我们看看CallBack接口的实现类如何定义:
1 private class MyCallback extends DiffUtil.Callback {
2 private ArrayList<Student> old_students, new_students;
3
4 MyCallback(ArrayList<Student> data, ArrayList<Student> students) {
5 this.old_students = data;
6 this.new_students = students;
7 }
8
9 @Override
10 public int getOldListSize() {
11 return old_students.size();
12 }
13
14 @Override
15 public int getNewListSize() {
16 return new_students.size();
17 }
18
19 // 判断Item是否已经存在
20 @Override
21 public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
22 return old_students.get(oldItemPosition).getNum() == new_students.get(newItemPosition).getNum();
23 }
24
25 // 如果Item已经存在则会调用此方法,判断Item的内容是否一致
26 @Override
27 public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
28 return old_students.get(oldItemPosition).getName().equals(new_students.get(newItemPosition).getName());
29 }
30 }
这里根据学号判断是否同一个Item,根据姓名判断这个Item是否有被修改。
实际上,这个Callback抽象类还有一个方法getChangePayload(),这个方法的作用是我们可以通过这个方法告诉Adapter对这个Item进行局部的更新而不是整个更新。
先要知道这个payload是什么?payload是一个用来描述Item变化的对象,也就是我们的Item发生了哪些变化,这些变化就封装成一个payload,所以我们一般可以用Bundle来充当。
接着,getChangePayload()方法是在areItemsTheSame()返回true,而areContentsTheSame()返回false时被回调的,也就是一个Item的内容发生了变化,而这个变化有可能是局部的(例如微博的点赞,我们只需要刷新图标而不是整个Item)。所以可以在getChangePayload()中封装一个Object来告诉RecyclerView进行局部的刷新。
假设上例中学号和姓名用不同的TextView显示,当我们修改了一个学号对应的姓名时,局部刷新姓名即可(这里例子可能显得比较多余,但是如果一个Item很复杂,用处就比较大了):
先是重写Callback中的该方法:
1 @Nullable
2 @Override
3 public Object getChangePayload(int oldItemPosition, int newItemPosition) {
4 Student newStudent = newStudents.get(newItemPosition);
5 Bundle diffBundle = new Bundle();
6 diffBundle.putString(NAME_KEY, newStudent.getName());
7 return diffBundle;
8 }
返回的这