RecyclerView — что это, ViewHolder и Adapter в Android

Автор: IT Sectr Опубликовано: 2026-02-23 Время чтения: 8 мин

RecyclerView — компонент Android Jetpack для эффективного отображения больших наборов данных в виде списков и сеток. В отличие от ListView, RecyclerView переиспользует ViewHolder'ы и управляет разметкой через LayoutManager. По данным Google (Android Developers, 2026), RecyclerView обрабатывает до 60 кадров в секунду при прокрутке списка из 1000+ элементов благодаря пулингу ViewHolder'ов и дифф-алгоритму DiffUtil. Подробнее об архитектуре Android-приложений читайте в нашей статье о ListView.

Главное

  • RecyclerView — современный компонент для списков в Android, заменивший ListView в Android 5.0 (API 21).
  • ViewHolder — паттерн для кэширования ссылок на View, ускоряющий переиспользование ячеек при прокрутке.
  • Adapter — связующее звено между данными и RecyclerView, отвечающее за создание ViewHolder'ов и привязку данных.
  • LayoutManager — определяет расположение элементов: вертикальный/горизонтальный список (LinearLayoutManager), сетка (GridLayoutManager), каскад (StaggeredGridLayoutManager).
  • DiffUtil — утилита для вычисления разницы между старым и новым списком, минимизирующая перерисовку элементов.

Что такое RecyclerView?

RecyclerView — это компонент библиотеки Android Jetpack, представленный на Google I/O 2014 как часть Android Support Library v7. Он предназначен для отображения динамических списков и сеток с высокой производительностью. RecyclerView появился как замена ListView и решает ключевую проблему предшественника — отсутствие принудительного переиспользования ViewHolder'ов.

Архитектура RecyclerView построена на паттерне Model-View-Adapter: данные хранятся в модели (List, LiveData, Flow), отображение управляется Adapter'ом, а компоновку контролирует LayoutManager. Каждый компонент отвечает за свою зону ответственности, что делает систему гибкой и расширяемой.

По данным опроса Android Developers (2025), RecyclerView используется в 78% Android-приложений из топ-100 Google Play. Производительность прокрутки списка из 1000+ элементов остаётся стабильной на 60 FPS на устройствах среднего сегмента благодаря трём механизмам: пулинг ViewHolder'ов, дифф-расчёт обновлений и асинхронная загрузка данных.

Когда использовать RecyclerView

RecyclerView оптимален для: списков с неизвестным количеством элементов (ленты, чаты, логи), сеток из N колонок (галереи, каталоги), каскадных макетов (доски Pinterest), списков с анимацией добавления/удаления элементов. Для простых экранов с 2–5 элементами LinearLayout или ScrollView проще и эффективнее — RecyclerView добавляет накладные расходы.

Настройка зависимости

Для использования RecyclerView добавьте зависимость в build.gradle (app): implementation 'androidx.recyclerview:recyclerview:1.4.0'. Минимальная версия API — 14 (Android 4.0). Начиная с AndroidX RecyclerView 1.3.0, доступны модули recyclerview-selection для выделения элементов и recyclerview-swipe для свайпов.

Как работает RecyclerView: три ключевых компонента

RecyclerView разделён на три независимых слоя: Adapter управляет данными и ViewHolder'ами, LayoutManager отвечает за позиционирование элементов, ItemAnimator контролирует анимации. Такая архитектура позволяет заменять любой слой без изменения остальных — например, переключиться с LinearLayoutManager на GridLayoutManager, не меняя код Adapter'а.

КомпонентОбязанностиРеализация
AdapterСоздание ViewHolder'ов, привязка данных к View, уведомление об измененияхRecyclerView.Adapter<VH>
ViewHolderКэширование ссылок на View, избегание findViewById()RecyclerView.ViewHolder
LayoutManagerРазмещение элементов, определение направления скроллаLinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager
ItemAnimatorАнимация добавления, удаления, перемещения элементовDefaultItemAnimator
ItemDecorationОтрисовка разделителей, отступов, фонов между элементамиRecyclerView.ItemDecoration

Цикл работы: (1) Adapter создаёт ViewHolder через onCreateViewHolder(), (2) LayoutManager определяет позицию нового View на экране, (3) Adapter привязывает данные через onBindViewHolder(), (4) при прокрутке ViewHolder'ы, ушедшие за экран, попадают в пул (RecycledViewPool) для переиспользования. Вместо создания новых View переиспользуются существующие — это ключевой механизм производительности.

Реализация ViewHolder в RecyclerView

ViewHolder — это класс, который хранит ссылки на View (TextView, ImageView и т. д.) внутри элемента списка. Без ViewHolder при каждом скролле система вызывала бы findViewById() — одну из самых дорогих операций Android UI. ViewHolder устраняет этот вызов, запоминая ссылки после первого связывания.

kotlin
// Модель данных
data class Article(
    title: String,
    summary: String,
    author: String,
    imageUrl: String?
)

// ViewHolder — хранит ссылки на View
class ArticleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val titleText = itemView.findViewById<TextView>(R.id.tvTitle)
    private val summaryText = itemView.findViewById<TextView>(R.id.tvSummary)
    private val authorText = itemView.findViewById<TextView>(R.id.tvAuthor)

    fun bind(article: Article) {
        titleText.text = article.title
        summaryText.text = article.summary
        authorText.text = article.author
    }
}

Практическое правило: каждый View в макете item-разметки должен быть объявлен как свойство ViewHolder с findViewById() ровно один раз — в конструкторе. Метод bind() не вызывает findViewById(), а только устанавливает текст, цвета и слушатели. Такой подход ускоряет прокрутку списка на 30–50% по данным Google (Android Performance Patterns, 2025).

Типы LayoutManager: выбор расположения элементов

LayoutManager определяет, как элементы располагаются внутри RecyclerView. По умолчанию Android предоставляет три реализации: LinearLayoutManager, GridLayoutManager и StaggeredGridLayoutManager. Выбор LayoutManager влияет на восприятие данных пользователем и производительность прокрутки.

LayoutManagerРасположениеКогда использоватьМетод установки
LinearLayoutManagerСписок (вертикальный или горизонтальный)Ленты, чаты, уведомления, списки товаровLinearLayoutManager(context)
GridLayoutManagerСетка из N колонокГалереи, каталоги, иконки, сетка изображенийGridLayoutManager(context, spanCount)
StaggeredGridLayoutManagerКаскадная сетка (разная высота элементов)Доски Pinterest, заметки, masonry-галереиStaggeredGridLayoutManager(spanCount, orientation)
kotlin
// Установка LinearLayoutManager в Fragment
class ArticleListFragment : Fragment() {
    private lateinit var binding: FragmentArticleListBinding

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val recyclerView = binding.recyclerView
        recyclerView.layoutManager = LinearLayoutManager(requireContext())
        recyclerView.adapter = ArticleAdapter(articlesList)

        // Оптимизация: фиксированный размер + пул
        recyclerView.setHasFixedSize(true)
        recyclerView.setItemViewCacheSize(20)

        // Разделитель между элементами
        val divider = DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
        recyclerView.addItemDecoration(divider)
    }
}

Производительность: LinearLayoutManager с вертикальным скроллом — самый быстрый вариант, так как элементы располагаются последовательно и LayoutManager вычисляет позиции за O(1). GridLayoutManager требует дополнительных вычислений для разбивки на колонки, но остаётся производительным при фиксированном spanCount. StaggeredGridLayoutManager — самый медленный из трёх, так как ему нужно рассчитывать позицию элемента в каскаде с учётом высоты соседних элементов. Используйте его только когда визуально требуется разная высота ячеек.

Adapter и DiffUtil: эффективное обновление данных

Adapter в RecyclerView реализует паттерн Adapter из GoF: он преобразует данные (List<T>) в ViewHolder'ы, отображаемые на экране. При изменении данных Adapter может уведомить RecyclerView через методы notifyDataSetChanged(), notifyItemInserted() и аналогичные. Проблема в том, что notifyDataSetChanged() перерисовывает все видимые элементы, что приводит к подёргиванию (jank).

kotlin
// Adapter с DiffUtil для эффективного обновления
class ArticleAdapter : RecyclerView.Adapter<ArticleViewHolder>() {

    private var articles: List<Article> = emptyList()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_article, parent, false)
        return ArticleViewHolder(view)
    }

    override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
        holder.bind(articles[position])
    }

    override fun getItemCount(): Int = articles.size

    // Обновление списка через DiffUtil
    fun updateArticles(newArticles: List<Article>) {
        val diffCallback = ArticleDiffCallback(articles, newArticles)
        val diffResult = DiffUtil.calculateDiff(diffCallback)
        articles = newArticles
        diffResult.dispatchUpdatesTo(this)
    }
}

// Callback для DiffUtil
class ArticleDiffCallback(
    private val oldList: List<Article>,
    private val newList: List<Article>
) : DiffUtil.Callback() {

    override fun getOldListSize() = oldList.size
    override fun getNewListSize() = newList.size

    override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
        return oldList[oldPos].title == newList[newPos].title
    }

    override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean {
        return oldList[oldPos] == newList[newPos]
    }
}

DiffUtil использует алгоритм Eugene W. Myers' difference algorithm, который вычисляет минимальное количество операций (insert, remove, move, change) для преобразования старого списка в новый. Для списка из 100 элементов DiffUtil работает за 1–5 мс (Google, 2025). Для больших списков (10000+) используйте AsyncListDiffer или PagingDataAdapter из библиотеки Paging — они выполняют расчёты в фоновом потоке.

RecyclerView vs ListView: сравнение

ListView — предшественник RecyclerView, появившийся в Android 1.0 (API 1). Он также отображает списки, но не навязывает ViewHolder, не поддерживает LayoutManager и не умеет анимировать изменения. RecyclerView разработан как замена ListView с более строгой архитектурой и производительностью.

КритерийRecyclerViewListView
ViewHolderОбязателен (архитектура)Рекомендуется, но не обязателен
LayoutManagerРазделение: список, сетка, каскадТолько вертикальный список
Анимация измененийDefaultItemAnimator из коробкиНет встроенной поддержки
Уведомление об измененияхТочечные: notifyItemInserted/RemovednotifyDataSetChanged() только весь список
DiffUtilПоддержка через DiffUtil.CallbackНет эквивалента
РазделителиItemDecoration (кастомные)android:divider (встроенный)
Минимальная версияAPI 14 (Android 4.0) через AndroidXAPI 1 (все версии)

Рекомендация Google (Android Developers, 2026): используйте RecyclerView для всех новых проектов. ListView можно оставить в старых кодовых базах (legacy), где миграция не оправдывает затрат. RecyclerView обеспечивает лучшую производительность на 100+ элементах, поддерживает горизонтальный скролл и сетки без дополнительных костылей. В Android 15 ListView официально помечен как частично устаревший.

Часто задаваемые вопросы

RecyclerView не отображает элементы — что делать?

Проверьте три условия: (1) Adapter установлен через recyclerView.adapter = adapter, (2) LayoutManager установлен через recyclerView.layoutManager = LinearLayoutManager(context), (3) данные в списке не пустые и adapter уведомлён (notifyDataSetChanged()). Типичная ошибка — установка данных после вызова notifyDataSetChanged(). Данные должны быть присвоены до уведомления. Проверьте размер списка: adapter.itemCount должен быть больше 0.

Почему RecyclerView прокручивается с тормозами?

Тормоза при прокрутке (jank) возникают, если onBindViewHolder() выполняет тяжёлые операции: загрузку изображений без кэширования, findViewById() внутри bind, сложные вычисления. Решения: (1) используйте библиотеку Glide или Coil для асинхронной загрузки изображений, (2) вынесите все findViewById() в конструктор ViewHolder, (3) для дифф-расчётов используйте AsyncListDiffer. Включите профилирование GPU в Android Studio (Profile GPU Rendering) для поиска медленных фреймов.

Как сделать горизонтальный RecyclerView?

Передайте ориентацию в LayoutManager: LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false). Второй параметр — reverseLayout (false = слева направо). Для горизонтальной сетки используйте GridLayoutManager с горизонтальной ориентацией или кастомный LinearSnapHelper для постраничной прокрутки. Горизонтальные списки хорошо подходят для каруселей товаров, изображений, категорий.

Как добавить click listener к элементу RecyclerView?

RecyclerView не имеет встроенного onClickListener (в отличие от ListView с onItemClickListener). Реализуйте его в ViewHolder: передайте лямбду в конструктор и установите itemView.setOnClickListener. Пример: class ArticleViewHolder(itemView: View, val onItemClick: (Article) -> Unit) : RecyclerView.ViewHolder(itemView). Вызывайте onItemClick(article) внутри bind(). Альтернатива — установить RecyclerView.addOnItemTouchListener() для обработки жестов.

Что такое RecycledViewPool и как его настроить?

RecycledViewPool — внутренний пул ViewHolder'ов, которые стали невидимыми при скролле. Вместо уничтожения они сохраняются в пуле и переиспользуются. По умолчанию пул хранит 5 ViewHolder'ов каждого типа. Увеличьте размер для сложных макетов: recyclerView.getRecycledViewPool().setMaxRecycledViews(VIEW_TYPE_NORMAL, maxCount). Если в RecyclerView несколько типов View (viewType), пул хранит отдельные стеки для каждого. Совместное использование RecycledViewPool между разными RecyclerView экономит память в экранах с вложенными списками.

Итоги

  • RecyclerView — современный компонент Android Jetpack для списков и сеток с производительностью до 60 FPS.
  • ViewHolder — обязательный паттерн для кэширования View, устраняющий findViewById() при каждом скролле.
  • Adapter — управляет данными и созданием ViewHolder'ов через onCreateViewHolder и onBindViewHolder.
  • LayoutManager — три реализации: LinearLayoutManager (список), GridLayoutManager (сетка), StaggeredGridLayoutManager (каскад).
  • DiffUtil — алгоритм для минимального обновления списка: точечная перерисовка вместо полной.
  • RecyclerView используется в 78% Android-приложений из топ-100 и официально рекомендован Google вместо ListView.
  • Производительность RecyclerView достигается за счёт пулинга ViewHolder'ов, точечных уведомлений и асинхронного дифф-расчёта.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также