0

I am now making a DiffUtil class to update only changed items in the RecyclerView.

I have seen several other sample code.

When comparing two objects, they compared unique values ​​such as id defined in the Model(Data) class in areItemsTheSame().

However, I think it is difficult to assign an id or unique value to the List, or the code is messy.

Do I have to define and compare id like this?

Do I really need to define a unique Id variable in the Model class that separates each object? Or shouldn't I use simply the equals()?

Using this Is it not just comparing the address of the object, but also the contents of the object?

As an additional question

What is the difference between DiffUtil.CallBack and DiffUtil.ItemCallBack?

This is my code.

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);
    }
}
ybybyb
  • 1,385
  • 1
  • 12
  • 33

2 Answers2

1

You got wrong the meaning of areItemsTheSame() and areContentsTheSame() callbacks. As you see, there are oldItemPosition and newItemPosition arguments in them. You should use them to compare specific items – not lists themselves.

In areItemsTheSame() you have to check whether model at "old" position in the old list equals a model at "new" position in the new list. This is how DiffUtil knows if it has to make reordering animations.

areContentsTheSame() will be called for two items if and only if you return true for them in the previous callback. Here you have to check whether visual representation of "old" and "new" models is the same. This is how DiffUtil knows if it has to make "item changing" animations.

To compare two models you have to override equals() and hashCode(). There you specify conditions under which you consider two models the same. For example, if they have same routine. I don't the know context of your task so I can't tell you exactly how to implement them, but usually you just compare all fields. Probably adding an id field is a good idea too. Then you can consider models "equal" if they have same id. And in hashCode() you can just return Objects.hash(id).

Now, speaking about your question about ItemCallback. Formally, here is the explanation from docs:

DiffUtil.Callback serves two roles - list indexing, and item diffing. ItemCallback handles just the second of these, which allows separation of code that indexes into an array or List from the presentation-layer and content specific diffing code.

Practically, ItemCallback just has less methods to implement and is used together with AsyncListDiffer. It's just because missing methods are already implemented under the hood in AsyncListDiffer.

alexal1
  • 394
  • 3
  • 7
  • Thank you for your detail answer. But Can i ask you a few question? 1. In the third paragraph, Does the Previous Callback means areItemsTheSame()? 2. What is the visual representation means? I don't know how to understand the meaning of this word. 3. If I don't use id when overriding the equality comparison of two objects in my code, can I do an equality comparison with a routine string? It is not necessary to compare all the members, right? – ybybyb Feb 02 '21 at 16:37
  • 1. Yes. 2. It means you want to redraw your list item. E.g. you compare items by `id` and it's the same, but you also have a field `routine` and it has been changed. So, items are the same, but contents are not. 3. Yes that's right. But think about the logic of your product. Is `routine` really unique for each model? If no, then you'll have problems: some list items will look the same while being actually different – alexal1 Feb 02 '21 at 18:20
  • However, when comparing the `hashCode` with a routine, even if the routine is not unique and returns true `areContentsTheSame()` compares the contents (routine, routineDetailModels), so it doesn't matter?? – ybybyb Feb 02 '21 at 18:39
  • It will just affect animations then – "changing" animations instead of "creating and removing" animations – alexal1 Feb 02 '21 at 20:33
  • Thank you so much. :) – ybybyb Feb 03 '21 at 15:45
1

You have to override the equals and hashcodes of your model classes.

RoutineModel:

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));
    }
}

And don't forget to override the equals and hashcode of your RoutineDetailModel.

Stoyan Milev
  • 725
  • 4
  • 17