Советы для профессионального использования RecyclerView. Часть 1

https://proandroiddev.com/recyclerview-pro-tips-part-1-8a291594bafc
  • Перевод

Советы для профессионального использования RecyclerView. Часть 1


Я решил написать эту статью, т.к. заметил, что многие разработчики допускают ошибки при использовании RecyclerView, даже несмотря на то, что Google уже довольно давно его выпустила.


Описанные здесь пункты упоминались в различных докладах и материалах на Google Devs.


В этой статье я буду кратко описывать ключевые пункты из этого видео, и вам не обязательно смотреть его прямо сейчас. Но рекомендую посмотреть его целиком после прочтения статьи.


1. Атрибут setHasFixedSize


Установите атрибут recyclerView.setHasFixedSize(true), когда recyclerView не планирует изменять размеры своих дочерних элементов динамически.


В результате recyclerView не будет перерисовываться каждый раз, когда в элементе списка обновятся данные, этот элемент перерисуется самостоятельно.


2. Click listener


Устанавливайте обработчик кликов в onCreateViewHolder(...).


Всякий раз, когда пользователь кликает по элементу списка, viewHolder сообщает позицию адаптера, где этот клик произошёл (vh.getAdapterPosition()). Это важно, потому что элементы могут перемещаться внутри адаптера, и связанные с ними view-компоненты не будут пересоздаваться.


В результате к тому времени, когда view-компонент будет создан, может произойти следующее: элемент списка будет находиться, допустим, на позиции 2, но когда пользователь кликнет на него, элемент будет уже на позиции 5. Таким образом, использование метода vh.getAdapterPosition() гарантирует получение корректного индекса списка.


3. Использование различных типов view-компонентов


Возвращайте напрямую layout в случае использования различных типов view-компонентов (например, R.layout.view_one).


Если ваш адаптер поддерживает различные типы view-компонентов, то метод getItemViewType и onCreateViewHolder будут выглядеть приблизительно так, как изображено ниже. Вам необходимо написать оператор switch внутри метода onCreateViewHolder для реализации нужной логики для соответствующих типов view-компонентов.


Стандартные getItemViewType и onCreateViewHolder


Но вместо этих типов, вы можете вернуть сразу layout. Это избавит вас от шаблонного кода в onCreateViewHolder:


Прокачанный onCreateViewHolder


Этот приём нельзя использовать постоянно, т.к. иногда вам может потребоваться более сложная логика внутри каждого выбранного layout для разных случаев. Но если это не ваш случай, то возвращаемые layout — это верный путь для работы с различными типами view-компонентов.


4. DiffUtil


Используйте DiffUtil для добавления новых данных в RecyclerView.


Всякий раз, когда данные в recyclerView меняются, большинство разработчиков вызывают метод notifyDataSetChanged() для отображения обновлённых данных на UI. Они просто не знают, что данный метод ресурсозатратен, и что именно здесь DiffUtil справляется куда эффективнее.


DiffUtil — это класс-утилита, который может вычислять разницу между двумя списками в виде списка обновлений, который затем конвертирует первый список во второй. Его можно использовать для вычисления обновлений в адаптере recyclerView. Чтобы использовать DiffUtil, вы должны реализовать DiffUtil.Callback, в котором есть несколько обязательных методов, необходимых для реализации логики DiffUtil:


Публичные методы DiffUtil.Callback


Самое большое преимущество DiffUtil заключается в том, что в RecyclerView вы можете обновить конкретный текст в TextView определённого элемента, вместо того, чтобы перерисовывать весь список. Для этого вам необходимо реализовать метод onChangePayload в DiffUtil.Callback. Есть очень хорошая статья на эту тему.


Во второй части рассмотрим другие советы качественного использования RecyclerView.


→ Советы для профессионального использования RecyclerView. Часть 2

Поделиться публикацией
Комментарии 8
    –4
    Советы по профессиональному использованию кривого мелкого кусочка API? Чиво?

    Из вашего паблика вк:
    Академия devcolibri устанавливает в рунете новую планку обучающих курсов по программированию.
    Скорее пробивает очередное дно

    Как же гуглоразрабы встаки любят костыли. Почему DiffUtil идет каким-то отдельным говноклассом а не прозрачно реализована внутри того же notifyDataSetChanged?
      +2
      Для статьи действительно подошло бы немного другое название.
      Но я не понимаю вашей агрессии. Google дает возможности подключения разного рода фич. А дальше разработчики сами решают нужны они или нет.
      Или вы считаете, что следует сразу напихать все в базовый класс? Судя по вашему комментарию вы, наверное, сразу всю библиотеку google play services подключаете к проекту.
        –2
        Google дает возможности подключения разного рода фич
        Нет, Google сделал херовую реализацию списочка, а когда понял что перфоманс никуда не годится, прикостылил сбоку DiffUtil

        Когда система перекладывает на разработчика реализацию части своего функционала, это говняный дизайн системы. Потому что в текущем виде разрабы не будут заморачиваться с DiffUtil, они просто сделают тормозное приложение и все, а вам потом этим пользоваться
          +1
          Позвольте с вами не согласится. Все познается в сравнении. Вот ListView без ViewHolder вот это действительно тормознутый списочек. А RecyclerView без DiffUtil… Разница, на малых объемах данных, не заметна.
      0
       Вмест DiffUtil можно использовать из коробки ListAdapter, хотя там тоже надо переопределить колбэк.
        0

        Еще можно добавить в список перегруженную версию метода onBindViewHolder(VH holder, int position, List<Object> payloads)

          0
          Очень интересно узнать, как правильно обновлять отдельные элементы списка. Например если мы имеем список загрузок и в каждом есть прогресс. Каким образом обновлять только один элемент, у которого обновился прогресс — делать это через PayLoads или каким-то образом хранить ссылку прямо на элемент списка?
            +1
            Если вы чётко знаете, на какой позиции находится элемент списка, то вы можете использовать метод адаптера notifyItemChanged(int position).

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое