вторник, 30 июля 2019 г.

Использование DiffUtil в RecyclerView

DiffUtil это класс-утилита который может вычислять разницу между двумя списками и выводить список операций обновления, который конвертирует первый лист во второй. Это может использоваться для вычисления обновлений адаптера RecyclerView.
В большинстве случаев наш список полностью меняется, и мы устанавливаем новый список в адаптер RecyclerView. И мы вызываем дорогостоящий notifyDataSetChanged для обновления адаптера. Класс DiffUtil решает эту проблему.

Давайте рассмотрим пример. Допустим, у нас есть список людей, в который мы добавляем случайные строки данных. Затем мы сортируем список по возрасту и обновляем RecyclerView.

Я собираюсь использовать статические данные для упрощения реализации.

public class DataProvider {

    public static List getOldPersonList(){
        List persons = new ArrayList<>();
        persons.add(new Person(1, 20, "John"));
        persons.add(new Person(2, 12, "Jack"));
        persons.add(new Person(3, 8, "Michael"));
        persons.add(new Person(4, 19, "Rafael"));
        return persons;
    }

    public static List sortByAge(List oldList){
        Collections.sort(oldList, new Comparator() {
            @Override
            public int compare(Person person, Person person2) {
                return person.age - person2.age;
            }
        });
        return oldList;
    }
}

DiffUtil.Callback является абстрактным классом и имеет 4 абстрактных метода и 1 неабстрактный. Давайте их рассмотрим.


Методы DiffUtil.Callback

getOldListSize() : возвращает размер старого списка

getNewListSize() : возвращает размер нового списка

areItemsTheSame(int oldItemPosition, int newItemPosition) : Вызывается DiffUtil чтобы решить являются ли два объекта представлениями одного и того же элемента. Если ваши элементы содержат уникальные идентификаторы, этот метод должен проверить их равенство

areContentsTheSame(int oldItemPosition, int newItemPosition) : Проверяет содержат ли два элемента одинаковые данные. Вы можете изменить это поведение в зависимости от вашего UI. Этот метод вызывается DiffUtil только если areItemsTheSame возвращает true.

getChangePayload(int oldItemPosition, int newItemPosition) : Если areItemTheSame возвращает true и areContentsTheSame возвращает false, DiffUtil вызывает этот метод, чтобы получить информацию об изменениях. В моем примере я не использую изменяемые объекты, но если вы хотите это использовать, вы может посмотреть этот пример.

public class MyDiffCallback extends DiffUtil.Callback {

    List oldPersons;
    List newPersons;

    public MyDiffCallback(List newPersons, List oldPersons) {
        this.newPersons = newPersons;
        this.oldPersons = oldPersons;
    }

    @Override
    public int getOldListSize() {
        return oldPersons.size();
    }

    @Override
    public int getNewListSize() {
        return newPersons.size();
    }

    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        return oldPersons.get(oldItemPosition).id == newPersons.get(newItemPosition).id;
    }

    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        return oldPersons.get(oldItemPosition).equals(newPersons.get(newItemPosition));
    }

    @Nullable
    @Override
    public Object getChangePayload(int oldItemPosition, int newItemPosition) {
        // вы можете вернуть определенное поле для измененного элемента
        return super.getChangePayload(oldItemPosition, newItemPosition);
    }
}

Метод обновления данных в моем RecyclerViewAdapter

public void updateList(ArrayList newList) {
    DiffUtil.DiffResult diffResult = 
            DiffUtil.calculateDiff(new MyDiffCallback(this.persons, newList));
    diffResult.dispatchUpdatesTo(this);
}

Это все. Мы вызываем метод dispactUpdatesTo (RecyclerView.Adapter), и адаптер будет уведомлен об изменении.

Комментариев нет: