Как устроены item, item layout, item page и адаптер в RecyclerView

RecyclerView отображает элементы через Adapter + ViewHolder: onCreateViewHolder создаёт view, onBindViewHolder заполняет его данными, а Recycler хранит пул для переиспользования; item — модель, item layout — XML-разметка, item page — либо экран детали, либо порция данных при пагинации.

Коротко о терминах и что нужно помнить

  • RecyclerView — контейнер, отвечающий за отрисовку, скролл и переиспользование view.
  • Item — логическая модель (сообщение, товар и пр.).
  • Item layout — XML для одного item (всё, что вёрстка показывает).
  • ViewHolder — кэш ссылок на view + bind-метод.
  • Adapter — мост между данными и RecyclerView: создаёт ViewHolder, привязывает данные, сообщает размер.

RecyclerView не знает и не хранит вашу бизнес-логику — он работает с количеством элементов и способами создания/заполнения view.

Жизненный цикл элемента: что реально происходит

  1. onCreateViewHolder(parent, viewType)
  • Вы инфлейтите item layout и создаёте ViewHolder.
  • Вызывается редко: примерно видимые элементы + запас для буфера.
  1. onBindViewHolder(holder, position)
  • Берёте модель по position и полностью настраиваете view.
  • Обязательно явно устанавливайте всё зависящее от данных (visibility, checked, тексты, изображения).
  • Не выполняйте тяжёлые операции (сети, долгие вычисления).

Если не сбрасывать/устанавливать состояние в onBindViewHolder, оно «перетечёт» между позициями при переиспользовании.

  1. Переиспользование (recycler pool)
  • Уходящий за экран ViewHolder попадает в пул и потом используется для другой позиции.
  • Для очистки ресурсов используйте onViewRecycled(holder) (отмена загрузки изображений, сброс слушателей).

Item layout: как проектировать для производительности

  • Минимизируйте вложенность — используйте ConstraintLayout или один уровень ViewGroup.
  • Избегайте wrap_content там, где можно задать 0dp + constraints или фиксированный размер.
  • Разделители рисуйте через ItemDecoration, а не в каждом item.
  • Лёгкие фоны и отсутствие лишних теней повышают FPS.

Сделайте item layout как можно проще — прямое и самое эффективное улучшение плавности скролла.

Пример: ImageView 40dp + два TextView с 0dp ширины и constraint-ограничениями — вариант для лёгкого и предсказуемого layout.

Adapter, паттерны и оптимизация обновлений

  • Храните логику отображения в ViewHolder.bind(item).
  • Для обновлений используйте ListAdapter + DiffUtil или применяйте точечные notifyItemInserted/Removed/Changed.
  • Не пересоздавайте адаптер при каждом обновлении — обновляйте список внутри адаптера.
  • Для нескольких типов элементов реализуйте getItemViewType и отдельные ViewHolder'ы.
  • Передавайте слушатели через конструктор адаптера; в колбэках проверяйте adapterPosition != NO_POSITION.

Оптимизации:

  • setHasFixedSize(true) если размер RecyclerView не зависит от контента.
  • Переиспользуйте один LayoutManager.
  • Для картинок используйте Coil/Glide с отменой запросов в onViewRecycled.

Пагинация и что такое item page

  • Item page в контексте списка — логическая порция данных (например, 20 элементов), загружаемая отдельно.
  • При прокрутке к концу триггерите загрузку следующей страницы, добавляете элементы в адаптер и уведомляете его.
  • Добавьте флаги isLoading/isLastPage и отдельный viewType для индикатора загрузки (footer).

Частые ошибки

  • Пересоздание адаптера при каждом обновлении данных.
  • Использование notifyDataSetChanged() вместо DiffUtil/точечных уведомлений.
  • Игнорирование очистки состояний view при bind.
  • Держать Context в полях адаптера/Holder без необходимости.
  • Запуск тяжёлой логики в onBindViewHolder (сеть, парсинг).

FAQ

  • Нужно ли отменять загрузку картинок? — Да: отменяйте в onViewRecycled или используйте библиотеку, которая делает это автоматически.
  • Что использовать для сложных списков с разными item? — Sealed-классы для моделей + несколько viewType и ViewHolder.
  • Как тестировать производительность? — Профилируйте GPU rendering, отслеживайте dropped frames и старайтесь упростить layout.

Если разделить ответственность: модели — в репозитории/ViewModel, отображение — в Adapter/ViewHolder, навигация — в Fragment/Activity, и применять DiffUtil + лёгкие item layout'ы, то RecyclerView работает предсказуемо и эффективно.