ViewHolder является не просто элементом кода, но и эффективной реализацией шаблона проектирования, интегрированной в RecyclerView для отображения списков. Суть шаблона ViewHolder заключается в том, что он эффективно "запоминает" различные view-элементы для каждого списка, сокращая тем самым время и ресурсы, которые бы потребовались для постоянного создания и поиска этих элементов.
Пример: предположим, у нас есть ViewHolder, предназначенный для отображения котика с определёнными характеристиками — рыжим окрасом и белым хвостом. При скроллинге списка этот котик исчезает из поля зрения пользователя. Вместо создания нового объекта ViewHolder для следующего котика, который, например, белый с черным хвостом, мы можем просто "перекрасить" уже существующий, невидимый в данный момент. Таким образом, экономятся системные ресурсы, так как избегается процесс создания нового объекта "с нуля".
Однако, есть и "ловушка": при переиспользовании ViewHolder'ов крайне важно не забывать обновлять все его атрибуты. В противном случае, пользователь может увидеть некорректные или устаревшие данные, что не только может сбить с толку, но и напугать нашего тестировщика, который потратит время на выяснение причин этого бага.
Пример использования:
Как это всё работает?
- Создание ViewHolder: при создании RecyclerView.Adapter мы определяем класс ViewHolder, который наследуется от RecyclerView.ViewHolder. Когда нам нужно показать нового котика в списке (при условии, что раньше котики не показывались или мы прокрутили до момента, когда все котики ещё заняты и не могут быть переиспользованы) вызывается метод onCreateViewHolder у адаптера. В этом методе адаптер создает новый экземпляр ViewHolder и связывает его с макетом котика. В конструкторе ViewHolder я использую viewBinding, который понадобится в методе bind, чтобы назначать котику какие-то параметры.
- Привязка данных: каждый раз, когда надо показать нового котика в списке и у нас есть котики, которые уже не видны, вызывается метод onBindViewHolder. В этом методе мы получаем ViewHolder и позицию элемента, а затем связываем данные с view внутри ViewHolder. Это позволяет обновить значения котика в соответствии с данными элемента списка.
- Переиспользование ViewHolder: RecyclerView самостоятельно управляет процессом переиспользования ViewHolder. Если один из котиков уходит с экрана, то он не пропадает. Когда следующий котик должен появиться на экране, берется старый котик и обновляется информация внутри него. Это делается через метод onBindViewHolder существующего ViewHolder.
- Удержание состояния: иногда во View могут быть важные детали, например, флажок рядом с котиком. Это состояние сохраняется даже тогда, когда котик уходит с экрана и возвращается обратно. Поэтому не забываем менять флажки и проверять данные.
Советы по использованию ViewHolder:
- Используйте ViewBinding: viewBinding помогает связать view элементов списка с ViewHolder, упрощая доступ к ним и предотвращая возможные ошибки при привязке данных.
- Ограничьте количество findViewById (а лучше вообще не используйте).
- Избегайте сложных вычислений: не выполняйте сложные вычисления или операции с большим объемом данных внутри ViewHolder. Лучше выполнить такие операции заранее и сохранить результаты в отдельной структуре данных, чтобы ViewHolder мог быстро получить доступ к ним.
- Не загружайте большие изображения: избегайте загрузки и отображения больших изображений внутри ViewHolder, особенно если список содержит множество элементов.
- Не выполняйте сетевые запросы: избегайте выполнения сетевых запросов внутри ViewHolder, так как это может привести к задержкам и блокировкам интерфейса.
Почему вообще не стоит выполнять сложные действия внутри ViewHolder?
- Ограниченное время выполнения: ViewHolder должен быть легковесным и быстрым. Он вызывается для каждого элемента списка во время прокрутки и перерисовки. Если внутри ViewHolder выполняются сложные операции, такие как вычисления или сетевые запросы, это может замедлить процесс отображения списка и список при прокрутке может тормозить.
- Потеря переиспользования: ViewHolder предназначен для переиспользования. Если внутри него выполняются операции, которые могут изменять его состояние или связанные с ним данные, это может привести к непредсказуемым результатам при повторном использовании. Например, если внутри ViewHolder загружаются изображения из сети, может возникнуть ситуация, когда изображение предыдущего элемента отображается для нового элемента из-за задержек в загрузке.
- Ухудшение производительности: Выполнение сложных операций внутри ViewHolder может привести к снижению производительности приложения, особенно если список содержит большое количество котиков. Это связано с тем, что каждый экземпляр ViewHolder будет потреблять больше ресурсов и времени на обработку.
Вместо выполнения сложных операций внутри ViewHolder, рекомендуется делегировать такие задачи ViewModel или другим компонентам. Пусть лучше пользователь подольше посмотрит на прогресс бар, чем когда у него будет тормозить список при прокрутке.
Дубль статей в телеграмме — https://t.me/android_junior
Мои заметки в телеграмме — https://t.me/android_junior_notes
P.S. сделано с помощью ChatGPT и Midjourney. :)