viewholder android что это

Очень часто мы хотим видеть список со значками. Для этого обычно создаётся разметка с TextView и ImageView, далее реализуется свой адаптер. Для небольших списков вам можно не заботиться о производительности списка, как правило, тормоза не ощущаются. Но если списки становятся слишком большими, то производительность резко падает. Почему так происходит?

При работе с большими списками следует быть осторожными, особенно, если вы создаёте собственные адаптеры с использованием картинок и других элементов. Вы можете легко превысить допустимые лимиты на память и получить ошибку в работе приложения. Это происходит из-за того, что в методе getView() сразу создаются объекты, занимающие память. Вот стандартный сценарий использования плохого адаптера, что называется «в лоб», когда в методе getView() происходит формирование элемента списка:

В этом примере происходит раздувание макета каждый раз, когда необходимо вернуть вид для отображения на экране. А происходит это при любой попытке прокрутить список.

Подобного кода следует избегать. Существует небольшая хитрость, чтобы снизить затраты и повысить производительность.

В методе getView() вторым параметром идёт convertView, который отвечает за выводимый компонент на экране. Когда формируется список и на экране появляются только видимые элементы списка, то параметр равен null. Когда мы начинаем прокручивать список, то верхний элемент становится невидимым, а контейнер для верхнего элемента списка перемещается вниз для следующего элемента. Происходит повторное использование одних и тех же контейнеров для элементов списка. При этом convertView принимает значение выводимого компонента.

Вы должны проверять convertView на наличие содержимого и переназначать его, отправляя новые данные в существующий шаблон, если convertView не пустой.

Система стирает элементы вашего списка, которые уже не отображаются на экране и передаёт управление ими в метод getView() через параметр convertView. Ваш адаптер может использовать этот вид и избежать «раздутие» шаблона для этого элемента. Это сохраняет память и уменьшает загрузку процессора.

Улучшенный вариант будет следующим:

В коде сравнивается convertView на null и уже в этом случае идет раздувание макета. Если не равно null, значит контейнер уже существует и мы можем просто переписать данные в нём. Производительность подобного решения почти в 2.5 раза выше, чем стандартное решение на списке из 10 тысяч записей.

ViewHolder

Создание элемента списка происходит по мере необходимости и производительность у данного решения чуть выше, чем у предыдущего примера.

Данные решения применимы к GridView и другим элементам, использующим адаптеры.

Напишем простой пример, чтобы наглядно увидеть переиспользование контейнеров для элементов списка. Добавьте на экран активности список ListView и подготовьте простой макет для элемента списка в файле res/layout/list_item.xml.

Напишем код для активности с адаптером.

Запустите пример и смотрите логи. Вначале вы увидите приблизительно такое:

Попробуйте тихонько сдвинуть список до появления следующего элемента. Как только первый элемент уйдёт за пределы экрана, то новый контейнер создаваться не будет и вместо null вы увидите ссылку на уже существующий контейнер.

Источник

Фантастические RecyclerView.ViewHolder и где они создаются

Давайте представим, что вы уже cоптимизировали ваш ресайклер вдоль и поперек:

вьюшки более плоские чем Земля

самый быстрый биндинг вьюхолдеров на Диком Западе

Но вам этого мало и вы продолжаете искать пути оптимизации. Поздравляю, вы попали в правильную статью!

Предыстория

Как-то раз, крася и двигая кнопки, я заметил, что ресайклер вью на одном из наших экранов немного проседал по отрисовке при активном скролле из-за большого разнообразия viewType (и, как следствие, частого создания новых вьюхолдеров), и, глядя на это, вспомнил о старой статье Chet Haase про то, как появилась возможность нагружать ресайклер работой по префетчингу элементов в моменты простоя мэйн трэда. Но мне показалось этого мало, и захотелось создавать элементы в моменты простоя. не мэйн трэда.

Читайте также:  можно ли заклеить унитаз и чем если он треснул

Так появилась идея подтюнить работу RecyclerView.RecycledViewPool для того, чтобы он заполнял себя вьюшками созданными off the main thread и отдавал их ресайклеру по запросу, т.е. занимался префетчингом.

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

Эту идею я решил разбить на две составляющие: одна будет заниматься созданием вьюшек в бэкграунде (Supplier), а другая — их получением и последующей передачей ресайклеру в пользование (Consumer).

RecyclerView.RecycledViewPool

Consumer

Прежде всего, мы хотим добавить в API нашего префетчера возможность конфигурировать количество вьюшек того или иного типа, которые следует. префетчить.

Далее, при попытке переработать вьюшку, мы будем сообщать вью пулу количество максимально возможных хранимых вьюшек, чтобы не хранить лишнего и актуализировать знание об этом числе, поскольку оно может меняться с течением времени.

Затем, когда к нашему вью пулу будет приходить запрос от ресайклера на получение ранее переработанной вьюшки, в случае если, в нашем пуле не окажется подготовленного/переработанного экземпляра, мы уведомим наш Supplier о том, что ресайклер сам создаст очередную вьюшку этого типа на мэйн трэде.

Осталось только связать жизненный цикл нашего вью пула и Supplier, и определить, что наш вью пул является Consumer‘ом префетчнутых вьюшек:

Здесь стоит отметить метод factorInCreateTime — это метод, который сохраняет время создания вьюхолдера, на основе которого GapWorker будет делать выводы о том, сможет он что-нибудь префетчнуть своими силами в моменты простоя мэйн трэда или нет.

Supplier

Запускаем очередь

Для запуска очереди на создание проверяем нет ли у нас в очереди уже достаточного количества вьюшек этого типа. Дальше проверим, не насоздавали ли мы уже достаточно вьюшек этого типа. И если оба эти условия проходят — добавляем в очередь запрос на то количество вьюшек, которое достаточно для того, чтобы довести количество уже созданных вьюшек до целевого значения count :

Создаем вьюшки

Что касается метода enqueueItemCreation — он будет абстрактным и его реализация будет зависеть от выбранного вашей командой подхода к многопоточности.

Здесь viewHolderProducer это простой typealias :

Стоит упомянуть, что традиционное создание вью через LayoutInflater.inflate — не самый эффективный способ утилизировать возможности представленного в статье механизма в силу синхронизации на аргументах конструктора.

Реагируем на создание вьюшки вне Supplier

В случае создания вьюшки за пределами нашего Supplier (метод onItemCreatedOutside ) — просто обновляем текущее знание о количестве уже созданных вьюшек данного типа:

Profit!

Источник

Максимально упрощаем работу с RecyclerView

На хабре уже полно статей на эту тему, все они в основном предлагают решения для удобного реюзинга ячеек в RecyclerView. Сегодня мы пойдем немного дальше и приблизимся к простоте сравнимой с DataBinding.

Читайте также:  Что такое лазерное удаление варикоза

Если вы еще не используете DataBinding для списков (хороший пример) и делаете это по старинке — то эта статья для вас.

Введение

Чтобы быстрее погрузиться в контекст статьи, давайте разберем что мы сейчас имеем при использовании универсального адаптера из предыдущих статей:

Чтобы каждая модель ячейки реализовала пустой интерфейс ViewModel:

Сделать классическую реализацию ViewHolder:

Инициализировать адаптер и передать ему необходимые данные:

Зная о DataBinding’e и его простоте реализации, возникает вопрос — зачем столько лишнего кода, ведь основное — это биндинг — сопоставление данных модели с лейяутом, от которого ни куда не уйти.

В классической реализации мы используем метод bindView(), все остальное это лишь подготовка к нему(реализация и инициализация ViewHolder).

Что такое ViewHolder и зачем он нужен?

Во фрагментах, активити и ресайклер вью мы часто используем этот паттерн, так для чего он нам нужен? Какие плюсы и минусы у него?

Создаем дефолтный ViewHolder

Если мы будем использовать стандартную реализацию RecyclerView.ViewHolder как заглушку в методе createViewHolder(), то каждый раз в bindView() мы будем вынуждены использовать метод findViewById, давайте пожертвуем плюсами и все-таки посмотрим что получится.

Так как класс абстрактный — добавим пустую реализацию нового дефолтного вьюхолдера:

Заменим в нашем ViewRender’e вьюхолдер:

Таким образом после первого вызова bindView() вьюхолдер будет знать о всех своих вьюхах и последующие вызовы будут использовать закэшированные значения.

Теперь вынесем все лишнее в базовый ViewRender, добавим новый параметр в конструктор для передачи ID лейяута и посмотрим что получилось:

С точки зрения количества кода выглядит гораздо лучше, остался один конструктор, который всегда одинаковый. А нужно ли нам каждый раз создавать новый ViewRenderer ради одного метода? Я думаю нет, решаем эту проблему через делегирование и дополнительный параметр в конструкторе, смотрим:

Добавление ячейки сокращается до:

Перечислим плюсы такого решения:

Заключение

Пожертвовав незначительными плюсами мы получили достаточно простое решение для добавления новых ячеек, это несомненно упростит работу с RecyclerView.

В статье приведен лишь простой пример для понимания, текущая реализация позволяет:

Источник

Избавляемся от рутины RecyclerView.Adapter с помощью DataBinding

RecyclerView — основной UI элемент практически любого приложения. Написание адаптеров и ViewHolder’ов зачастую слишком рутинная работа и содержит достаточно boilerplate кода. В этой статье я хочу показать как с использованием DataBinding и паттерна MVVM можно написать абстрактный адаптер и напрочь забыть про ViewHolder’ы, inflate, ручной биндинг и прочую рутину.

ViewHolder

Мы все привыкли писать отдельный ViewHolder под каждый тип ячеек в таблице для хранения ссылок на отдельные вьюшки и связывания данных.

Можно сказать что DataBinding генерирует на лету тот код, что вы обычно пишите в ViewHolder’ах, поэтому надобность в них отпадает и мы легко можем использовать одну реализацию, хранящую в себе объект готового биндинга:

ViewDataBinding это базовый абстрактный класс для всех сгенерированных классов DataBinding’а и хоть мы и передаем его параметром шаблона для метода bind, DataBindingUtil сам поймет какой layout мы используем и какую реализацию в итоге использовать.

ViewModelAdapter

Разобравшись с ViewHolder’ом надо определиться чего мы хотим от нашего базового адаптера в итоге. Все, что мне требуется от адаптера в пределах MVVM архитектуры — отдать список объектов (ViewModel’ей), сказать какую разметку я хочу использовать для данных в этом списке классов и совершенно не беспокоиться о необходимой для этого логике.

Читайте также:  какие страны открыты для русских туристов на данный момент

Логику привязки данных на себя берет DataBinding, но это уже совершенно другая статья, коих в интернете уже достаточно.

Напишем логику для конфигурации нашего адаптера:

Для каждого класса объектов таблицы будем хранить пару layoutId и bindingId.

Остается лишь реализовать абстрактные функции RecyclerView.Adapter:

На этом вся основная логика ViewModelAdapter описана, однако остается одна проблема — обработка кликов по ячейкам. Обычно эту логику описывают в Activity, но я не любитель транслировать логику вверх по иерархии, если без этого ну никак не обойтись, поэтому реализую ее прямо в адаптере, но вы можете реализовывать ее там где вам удобно.

Для реализации обработки кликов добавим в ViewModelAdapter такое понятие как sharedObject, объект который будет биндится на все ячейки таблицы (не обязательно, если в разметке не найдет variable с данным bindingID ничего не упадет).

Теперь рассмотрим как это все в итоге работает:
Как пример я реализовал адаптер для бокового меню (используйте NavigationView из стандартной библиотеки если у вас нет необходимости отойти от Material Design).

И как пример layout: cell_navigation_item.xml

Как видите все достаточно просто, нет никакой лишней логики. Мы можем объявлять сколько угодно типов ячеек вызовом 1 функции. Мы можем позабыть о ручном связывании данных для UI.

Данный адаптер успешно проходит боевые испытания на протяжении полугода в нескольких крупных проектах.

С удовольствием отвечу на ваши вопросы в комментариях.

Источник

Долгожданный View Binding в Android

Пару дней назад Google выпустил Android Studio 3.6 Canary 11, главным нововведением в которой стал View Binding, о котором было рассказано еще в мае на Google I/O 2019.

Как включить

Чтобы включить View Binding в модуле надо добавить элемент в файл build.gradle :

Также можно указать, что для определенного файла разметки генерировать binding класс не надо. Для этого надо указать аттрибут tools:viewBindingIgnore=»true» в корневой view в нужном файле разметки.

Как использовать

Каждый сгенерированный binding класс содержит ссылку на корневой view разметки ( root ) и ссылки на все view, которые имеют id. Имя генерируемого класса формируется как «название файла разметки», переведенное в camel case + «Binding».

Например, для файла разметки result_profile.xml :

Позже binding можно использовать для получения view:

Отличия от других подходов

Главные преимущества View Binding — это Null safety и Type safety.

А вообще, было бы удобно, если бы сгенерированное поле имело наиболее возможный специфичный тип. Например, чтобы для Button в одной конфигурации и TextView в другой генерировалось поле с типом TextView ( public class Button extends TextView ).

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

Использование в RecyclerView.ViewHolder

Ничего не мешает использовать View Binding при создании view для RecyclerView.ViewHolder :

Однако, для создания такого ViewHolder придется написать немного бойлерплейта:

В целом, View Binding это очень удобная вещь, которую легко начать использовать в существующих проектах. Создатель Butter Knife уже рекомендует переключаться на View Binding.

Немного жаль, что такой инструмент не появился несколько лет назад.

Источник

Информ портал о технике и не только