Найти тему

ViewHolder в RecyclerView: что это и как работает?

Оглавление

ViewHolder является не просто элементом кода, но и эффективной реализацией шаблона проектирования, интегрированной в RecyclerView для отображения списков. Суть шаблона ViewHolder заключается в том, что он эффективно "запоминает" различные view-элементы для каждого списка, сокращая тем самым время и ресурсы, которые бы потребовались для постоянного создания и поиска этих элементов.

Пример: предположим, у нас есть ViewHolder, предназначенный для отображения котика с определёнными характеристиками — рыжим окрасом и белым хвостом. При скроллинге списка этот котик исчезает из поля зрения пользователя. Вместо создания нового объекта ViewHolder для следующего котика, который, например, белый с черным хвостом, мы можем просто "перекрасить" уже существующий, невидимый в данный момент. Таким образом, экономятся системные ресурсы, так как избегается процесс создания нового объекта "с нуля".

Однако, есть и "ловушка": при переиспользовании ViewHolder'ов крайне важно не забывать обновлять все его атрибуты. В противном случае, пользователь может увидеть некорректные или устаревшие данные, что не только может сбить с толку, но и напугать нашего тестировщика, который потратит время на выяснение причин этого бага.

Пример использования:

https://gist.github.com/Ladgertha/488208d468246de7096a0ab8d5e416fc (код не претендует на идеальный и сделан для примера)
https://gist.github.com/Ladgertha/488208d468246de7096a0ab8d5e416fc (код не претендует на идеальный и сделан для примера)

Как это всё работает?

  1. Создание ViewHolder: при создании RecyclerView.Adapter мы определяем класс ViewHolder, который наследуется от RecyclerView.ViewHolder. Когда нам нужно показать нового котика в списке (при условии, что раньше котики не показывались или мы прокрутили до момента, когда все котики ещё заняты и не могут быть переиспользованы) вызывается метод onCreateViewHolder у адаптера. В этом методе адаптер создает новый экземпляр ViewHolder и связывает его с макетом котика. В конструкторе ViewHolder я использую viewBinding, который понадобится в методе bind, чтобы назначать котику какие-то параметры.
  2. Привязка данных: каждый раз, когда надо показать нового котика в списке и у нас есть котики, которые уже не видны, вызывается метод onBindViewHolder. В этом методе мы получаем ViewHolder и позицию элемента, а затем связываем данные с view внутри ViewHolder. Это позволяет обновить значения котика в соответствии с данными элемента списка.
  3. Переиспользование ViewHolder: RecyclerView самостоятельно управляет процессом переиспользования ViewHolder. Если один из котиков уходит с экрана, то он не пропадает. Когда следующий котик должен появиться на экране, берется старый котик и обновляется информация внутри него. Это делается через метод onBindViewHolder существующего ViewHolder.
  4. Удержание состояния: иногда во View могут быть важные детали, например, флажок рядом с котиком. Это состояние сохраняется даже тогда, когда котик уходит с экрана и возвращается обратно. Поэтому не забываем менять флажки и проверять данные.

Советы по использованию ViewHolder:

  1. Используйте ViewBinding: viewBinding помогает связать view элементов списка с ViewHolder, упрощая доступ к ним и предотвращая возможные ошибки при привязке данных.
  2. Ограничьте количество findViewById (а лучше вообще не используйте).
  3. Избегайте сложных вычислений: не выполняйте сложные вычисления или операции с большим объемом данных внутри ViewHolder. Лучше выполнить такие операции заранее и сохранить результаты в отдельной структуре данных, чтобы ViewHolder мог быстро получить доступ к ним.
  4. Не загружайте большие изображения: избегайте загрузки и отображения больших изображений внутри ViewHolder, особенно если список содержит множество элементов.
  5. Не выполняйте сетевые запросы: избегайте выполнения сетевых запросов внутри ViewHolder, так как это может привести к задержкам и блокировкам интерфейса.

Почему вообще не стоит выполнять сложные действия внутри ViewHolder?

  1. Ограниченное время выполнения: ViewHolder должен быть легковесным и быстрым. Он вызывается для каждого элемента списка во время прокрутки и перерисовки. Если внутри ViewHolder выполняются сложные операции, такие как вычисления или сетевые запросы, это может замедлить процесс отображения списка и список при прокрутке может тормозить.
  2. Потеря переиспользования: ViewHolder предназначен для переиспользования. Если внутри него выполняются операции, которые могут изменять его состояние или связанные с ним данные, это может привести к непредсказуемым результатам при повторном использовании. Например, если внутри ViewHolder загружаются изображения из сети, может возникнуть ситуация, когда изображение предыдущего элемента отображается для нового элемента из-за задержек в загрузке.
  3. Ухудшение производительности: Выполнение сложных операций внутри ViewHolder может привести к снижению производительности приложения, особенно если список содержит большое количество котиков. Это связано с тем, что каждый экземпляр ViewHolder будет потреблять больше ресурсов и времени на обработку.

Вместо выполнения сложных операций внутри ViewHolder, рекомендуется делегировать такие задачи ViewModel или другим компонентам. Пусть лучше пользователь подольше посмотрит на прогресс бар, чем когда у него будет тормозить список при прокрутке.

-2

Дубль статей в телеграмме — https://t.me/android_junior

Мои заметки в телеграмме — https://t.me/android_junior_notes

P.S. сделано с помощью ChatGPT и Midjourney. :)