Android recyclerview databinding viewmodel

Android Data Binding for RecyclerView: flexible way

Со времени первого анонса на Google IO 2015 новой библиотеки Data Binding Library прошло немало времени. Появилось много примеров, много гайдов и много исправлений и доделок в самой библиотеке. Вот уже и биндинг стал two-way, и ссылаться на другие View по их id можно в самом layout-файле да и армия поклонников этой библиотеки неуклонно растет. И, наверное, каждый новый адепт начинает с поиска примеров — как правильно использовать так чтобы и удобно, и меньше кода, и по-феншуй. Если сейчас вбить запрос на подобии «Android DataBinding + RecyclerView» то, наверняка, получим целую кучу ссылок на различные гайды. Даже на Хабре уже была подобная статья — Android Data Binding in RecyclerView.

Но не смотря на такое обилие ресурсов/гайдов, многие из них показывают базовый функционал, и каждый разработчик, начиная активно использовать Data Binding, придумывает свой, удобный ему способ работы. Далее будет показан один из таких способов.
Пример тут: DataBinding_For_RecyclerView

Этапы:

— реализация/настройка Адаптера (viewTypes, items, обработка кликов по элементам и внутри самих элементов списка);
— настройка RecyclerView (задать LayoutManager, Adapter, ItemDecorator, ItemAnimator, item divider size, ScrollListener, . ).

Конфигурирование RecyclerView

Оставим пока реализацию адаптера и рассмотрим способ задания конфигурации самого RecyclerView. Самый простой здесь способ, просто присвоить id для RecyclerView и уже в коде задать все параметры:

Второй, часто встречаемый, способ — сделать часть самых банальных инициализаций в коде, например, так:

И наконец, используемый автором способ — использовать класс-посредник, который будет сконфигурирован в коде и применен через биндинг. Профит такого подхода в возможности скрыть «дефолтные» настройки внутри класса-посредника (хелпера), но при этом иметь полный контроль над конфигурированием RecyclerView в одном месте.

Реализация гибкого адаптера

Один из самых интересных вопросов — наследование или композиция. Многие повторяют как мантру «Предпочитай композицию наследованию», но все равно продолжают и дальше плодить наследников от наследников от наследников… Кто еще не знаком с потрясающей статьей на эту тему в применении к Адаптерам списков, обязательно просмотрите — JOE’S GREAT ADAPTER HELL ESCAPE. Если кратко, то представим такую ситуацию: нам дают задание реализовать простенькое приложение с 2-мя списками: список пользователей (User) и список локаций (Location). Ничего сложного, правда?) Создаем два адаптера, — UserAdapter и LocationAdapter, — и, по сути, все. Но тут, в следующем «спринте» (мы же по Agile, верно? ) заказчик хочет добавить еще и рекламу в каждый из этих списков (Advertisment).

Никаких проблем, говорим мы, создаем еще один адаптер AdvertismentAdapter и наследуем от него оба предыдущих: UserAdapter extends AdvertismentAdapter и LocationAdapter extends AdvertismentAdapter. Все хорошо, все рады, но вот в новом «спринте» клиент хочет еще один список, где будут смешаны все 3 сущности сразу. Как быть теперь?

И вот тут и переходим от наследования к композиции. До этого у нас на каждый список был отдельный адаптер со своими типами (viewTypes), теперь заменим эту систему на один адаптер и 3 делегата на каждый тип элемента списка. Адаптер не будет ничего знать о типах элементов, которые отображает, но знает, что у него есть несколько делегатов, спросив по очереди каждый из которых, можно найти конкретный для нужного элемента списка и делегировать ему создание этого элемента.

В таком случае, нам уже абсолютно все равно сколько списков и с какими типами элементов будут, любой список формируется как конструктор — набором делегатов.

Что касается DataBinding, то вся магия — в особом ViewHolder:

Если же, даже лень создавать отдельный делегат для каждого нового типа/вида элемента списка, можно воспользоваться особенностью биндинга и использовать единый универсальный делегат для любого типа:

Читайте также:  Скайп для андроида как установить

Обработку кликов по элементам несложно реализовать, передав через биндинг обработчик кликов, например, как описано тут — Android и Data Binding: обработка действий, или использовав любой другой, удобный для вас, способ.

Заключение

Таким образом, используя Android Data Binding Library, реализация списков становиться совершенно обыденной вещью. Даже не нужно писать реализацию показанных выше вещей, а просто импортировав готовую библиотеку автора, или просто «скопипастив» их оттуда.

Источник

Android Data Binding in RecyclerView

На Google IO 2015 анонсировали новую библиотеку Data Binding Library. Основная ее задача — вынесения взаимодействия модели и View в xml-файлы. Она значительно упрощает написание кода и избавляет от необходимости использования методов findByViewId(), добавления ссылок на view-элементы внутри Activity/Fragment’ов. Также она позволяет использовать кастомные атрибуты, привязывая их к статическим методам. Поскольку статьей просто по Data Binding уже достаточно, но по его использованию в RecycleView всего ничего, восполним этот пробел.

Настройка

Для начала заходим в файл build.gradle, который лежит в корневом каталоге проекта. В блоке dependencies выставляем:

Далее подключим Data Binding плагин к проекту. Для этого в build.gradle добавляем строчку с плагином. Также проверяем, чтобы compileSdkVersion была 23.

Биндинг

Перейдем к созданию xml-файла. Он, как обычно, создается в паке res/layoyt. В качестве корневого тега используем layout. Android Studio может подсвечивать его красным или предлагать выставить ширину и высоту, но мы ее игнорируем.

Чтобы создался биндер-класс, который и будет привязывать модель к view, нужно привязать xml к модели. Для этого внутри тега указываем имя и путь к нашей модели. В качестве примера будет отображатьcя список фильмов.

Осталось добавить свой layout и привязать к нему модель. Пусть у каждого фильма будет картинка, заголовок и краткое описание. Чтобы указать, что поле будет считываться из модели используем “@<*какое поле из модели использовать*>”.

С android:text=»@» и android:text=»@» все понятно — просто в качестве текста будет показано соответствующее поле, но что на счет app:imageUrl=»@«? Тут начинается реальная магия Data Binding. Вы можете добавлять сколько угодно кастомных атрибутов и даже не прописывать их в atts.xml, а аннотация @BindingAdapter() поможет вам их обработать. Ниже будет показано, как обрабатывать такие аннотации.

Перейдем к адаптеру. Напишем простой RecyclerView.Adapter. Начнем с ViewHolder. Как он выглядел раньше:

Как он выглядел после Butter Knife:

Как он выглядит после DataBinding:

Далее нас интересуют два основных метода адаптера: onCreateViewHolder и onBindViewHolder. Созданием и биндигом будет заниматься MovieItemBinding. Он генерируется по названию xml, который мы написали выше. В данном случае файл xml назывался movie_item.xml.

Теперь перейдем к onBindViewHolder, как он выглядел раньше:

Как он выглядит теперь:

Но это еще не всё, как на счет кастомного app:imageUrl=»@«. Опять же все просто: внутри адаптера делаем статический метод с аннотацией @BindingAdapter. Внутрь аннотации передаем наш аттрибут. В итоге получаем

На вход поступит imageView и то, что передаст модель в качестве image. Теперь все заработает.

Остальные полезности

В модели Movie была переменная isWatched. Допустим, мы хотим, чтобы у просмотренных и новых фильмов были разные обработчики на клик. С DataBinding’ом теперь это проще простого. Напишем обработчик нажатия для фильма.

Добавим его в xml-файл в тег data.

Теперь в методе адаптера onBindViewHolder можно засетить наш лисенер. Как и в случае с биндером, название метода генерируется соотвественному названию переменной в xml-файле.

Пусть по загрузке картинка у просмотренных фильмов будет черно-белая. Для преобразование картинки добавим новый атрибут.

В адаптере через @BindingAdapter реализуем обработку

Также очень удобно использовать стабовые значения, если одно из полей пустое.

Стоит также отметить, что внутри MovieItemBinding содержатся ссылки на все view, у которых есть ID в xml-файле.

Библиотека очень упрощает работу с RecycleView, количество кода при написании теперь уменьшается в разы, при этом никаких if/else для колбеков и данных. С JavaRX можно еще больше упростить обновление данных, пока правда оно работает только в одну сторону: при изменении данных обновляется UI, но не наоборот.

Читайте также:  Аирподсы для андроид инструкция

Источник

Избавляемся от рутины 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.

  • layoutId — как понятно из имени и аннотации @LayoutRes это соответствующая разметка ячейки.
  • bindingId — это сгенерированный идентификатор переменной, используемый в соответствующей разметке. Он нам понадобится для того, чтобы забиндить объект таблицы в написанный ранее ViewHolder, а точнее в ViewDataBinding.

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

  • getItemViewType — так как layoutId уникален для разных ячеек мы с легкостью можем использовать его как viewType.
  • onCreateViewHolder — не забываем что viewType это наш layoutId.
  • onBindViewHolder — все что требуется для привязки данных объекта к разметке — сообщить DataBinding’у о том, что в данной ячейке теперь новый объект, всю остальную логику он возьмет на себя.

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

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

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

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

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

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

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

Источник

Легкий DataBinding для Android

Здравствуйте уважаемые читатели. Все мы любим и используем DataBinding, который представила компания Google несколько лет назад, для связи модели данных с вьюшками через ViewModel. В этой статье, хочу поделиться с вами, как можно унифицировать этот процесс с помощью языка Kotlin, и уместить создание адаптеров для RecyclerView (далее RV), ViewPager и ViewPager2 в несколько строчек кода.

Читайте также:  Виджеты для андроид где находится

Начну с того, что раньше разрабатывали кастомные адаптеры, которые под капотом создавали ViewHolder’ы, и их написание, а тем более поддержка, занимала достаточно большое количество времени. Ниже приведу пример типичного адаптера для RV:

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

Затем появился DataBinding и большую часть по связыванию данных перекладывалась на него, но адаптеры все равно приходилось писать вручную, изменились только методы onCreateViewHolder , где вместо инфлэйтинга через LayoutInflater , использовался DataBindingUtil.inflate , а при создании вьюхолдеров данные связывались непосредственно с самой вьюшкой через ссылку на созданный объект байдинга.

Выглядит уже лучше, но что если в RV, по прежнему должны отображаться элементы лайаута с разными типами данных, то такая реализация не сильно помогла решить проблему больших адаптеров. И здесь на помощь приходит аннотация BindingAdapter из библиотеки androidx.databinding. С ее помощью, можно создать универсальное решение, которое скрывает реализацию создания адаптера для RV, если использовать вспомогательный объект-конфигуратор DataBindingRecyclerViewConfig , в котором содержится ряд свойств для настройки адаптера.

В результате на свет появилась библиотека, которая называется EasyRecyclerBinding. В нее так же вошли BindingAdapters для ViewPager и ViewPager2. Теперь процесс связывания данных выглядит следующим образом:
1) В лайауте фрагмента, необходимо добавить специальные переменные, которые содержат список отображаемых моделей данных и конфигурацию, указав их атрибутами для RV, — app:items и app:rv_config .

ViewModel, соответственно, содержит список моделей данных для адаптера, которые должны отображаться в RV, а фрагмент конфигурацию DataBindingRecyclerViewConfig.

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

2) Во фрагменте нам нужно получить конфигурацию для адаптера и передать её в отображение через инстанс dataBinding, используя специальную функцию-конструктор createRecyclerConfig , которая создаст и вернет инстанс DataBindingRecyclerViewConfig, указав при этом id лайаута для выбранной модели, и название свойства, к которому будет прикреплена данная модель.

Это все, что нужно сделать, чтобы связать данные из ViewModel с отображением списка в RV. Так же при создании адаптера можно назначить слушатели событий для байдинга вьюхолдера, такие как onItemClick, onItemCreate, onItemBind и другие.

А чтобы использовать вьюхолдеры с разными визуальными лайаутами, к которым привязаны свои модели отображения данных, необходимо имплементировать в них специальный интерфейс из библиотеки EasyRecyclerBinding — IBindingModel и переопределить поле layoutResId , — id лайаута, который будет отображаться для этой модели в списке.

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

Таким образом, создание адаптеров для отображения данных в RV, превратилось в простую задачу состоящую из пары строчек кода, где разработчику уже не надо думать о том, как поддерживать, фактически, не нужную часть presentation слоя. И даже, если модель данных изменится, достаточно будет поменять только её отображение, и связать его с новыми данными, не заботясь о поддержке адаптеров.

Аналогичный процесс создания адаптеров для ViewPager и ViewPager2, представлен в примере на github вместе с открытым кодом, ссылку на который, я разместил в конце статьи. В настоящий момент библиотека еще дорабатывается, и хочется получить адекватный фидбек, и пожелания по дальнейшему ее развитию. Так же в неё вошли вспомогательные функции для удобного создания байдинга, в том числе в связке с ViewModel. (LayoutInflater.createBinding, Fragment.createBindingWithViewModel, etc)

Спасибо, что дочитали до конца. Приятного кодинга и хорошего настроения)

Источник

Оцените статью