← Назад к вопросам

Для чего нужен паттерн ViewHolder?

1.3 Junior🔥 171 комментариев
#UI и вёрстка

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Назначение паттерна ViewHolder

ViewHolder — это классический паттерн оптимизации, применяемый в Adapter-ах RecyclerView (а ранее и в ListView) в Android для минимизации операций поиска представлений (findViewById) и повторного использования уже созданных объектов представлений.

Основная проблема, которую решает ViewHolder

При прокрутке списка метод onBindViewHolder (или getView в ListView) вызывается очень часто для каждого нового элемента, появляющегося на экране. Без ViewHolder код каждый раз выполнял бы ресурсоёмкий поиск виджетов в иерархии layout'а:

// ПЛОХО: Поиск представлений при каждой привязке данных (старый подход для ListView)
public View getView(int position, View convertView, ViewGroup parent) {
    View view = convertView;
    if (view == null) {
        view = inflater.inflate(R.layout.list_item, parent, false);
        // Каждый раз ищем TextView
    }
    TextView title = view.findViewById(R.id.titleTextView); // Дорогая операция!
    title.setText(data.get(position).getTitle());
    return view;
}

Операция findViewById обходит всё дерево виджетов элемента списка, что требует значительных вычислительных ресурсов. При быстрой прокрутке это приводит к подрывам (jank) и потере плавности интерфейса.

Как работает ViewHolder

Паттерн предлагает хранить ссылки на все необходимые представления в специальном объекте-контейнере:

// Класс ViewHolder для хранения ссылок на View
class BookViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    // Ссылки инициализируются ОДИН РАЗ при создании
    val titleTextView: TextView = itemView.findViewById(R.id.titleTextView)
    val authorTextView: TextView = itemView.findViewById(R.id.authorTextView)
    val coverImageView: ImageView = itemView.findViewById(R.id.coverImageView)
}

// В адаптере
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookViewHolder {
    val view = LayoutInflater.from(parent.context).inflate(R.layout.item_book, parent, false)
    return BookViewHolder(view) // findViewById вызывается здесь один раз!
}

override fun onBindViewHolder(holder: BookViewHolder, position: Int) {
    val book = books[position]
    holder.titleTextView.text = book.title // Прямой доступ, без поиска!
    holder.authorTextView.text = book.author
    // Загрузка изображения в уже найденный ImageView
    Glide.with(holder.coverImageView).load(book.coverUrl).into(holder.coverImageView)
}

Ключевые преимущества паттерна

  • Значительное повышение производительности. Операция findViewById выполняется только при создании нового элемента (onCreateViewHolder), а не каждый раз при его привязке к данным. Для элементов, выходящих за пределы экрана и затем возвращающихся, повторного поиска не происходит — используются сохранённые ссылки из ViewHolder.
  • Сокращение нагрузки на сборщик мусора (Garbage Collector). Объекты View не создаются постоянно, а переиспользуются. Создаётся и хранится в памяти только пул ViewHolder-ов, необходимый для отображения на экране.
  • Улучшение организации кода. Логика работы с представлениями элемента списка инкапсулирована в одном классе. Это делает код адаптера чище, а доступ к виджетам — типобезопасным.
  • Прямой доступ к View. В onBindViewHolder мы работаем не с абстрактным itemView, а с конкретными, заранее известными полями (titleTextView, coverImageView), что упрощает код и снижает вероятность ошибок.

Эволюция и современный контекст

С появлением биндинга данных (View Binding) и особенно Data Binding ручное создание классов ViewHolder стало менее необходимым, так как эти технологии автоматически генерируют классы, содержащие ссылки на все View. Однако принцип остаётся тем же: поиск представлений происходит один раз, а результаты кэшируются.

// Использование View Binding в ViewHolder (современный подход)
class BookViewHolder(private val binding: ItemBookBinding) : RecyclerView.ViewHolder(binding.root) {
    fun bind(book: Book) {
        binding.titleTextView.text = book.title // Binding уже хранит ссылки
        binding.authorTextView.text = book.author
        binding.executePendingBindings()
    }
}

Вывод: Паттерн ViewHolder является фундаментальным для реализации плавной и отзывчивой прокрутки списков в Android. Он решает проблему производительности за счёт кэширования ссылок на виджеты, устраняя необходимость в многократном вызове findViewById, и лежит в основе эффективной работы RecyclerView.