Android data binding recyclerview

Android Data Binding: RecyclerView

Reduce, Reuse, Rebind

While sometimes I like to think so, Data Binding as a term doesn’t always mean Android Data Binding. RecyclerView has its own way of binding data to the UI. RecyclerView has an Adapter with two very important methods that we implement to bind data:

RecyclerView exposes the common ViewHolder pattern as a first class citizen in its API. In onCreateViewHolder(), the Views are created and the ViewHolder contains references to them so that the data can be set quickly. Then in onBindView(), the specific data is assigned to the Views.

Android Data Binding in RecyclerView

As discussed in a previous article, Android Data Binding can be treated like the ViewHolder pattern. Ideally, we’d just return the generated Binding class from our onCreateViewHolder(), but it doesn’t extend RecyclerView.ViewHolder. So, the binding class will have to be contained by the ViewHolder instead.

Now, my adapter can create and bind using Android Data Binding:

If you were looking closely, you saw the executePendingBindings() at the end of MyViewHolder.bind(). This forces the bindings to run immediately instead of delaying them until the next frame. RecyclerView will measure the view immediately after onBindViewHolder. If the wrong data is in the views because the binding is waiting until the next frame, it will be measured improperly. The executePendingBindings() is important!

Reusing the ViewHolder

If you’ve ever used a RecyclerView’s ViewHolder before, you can see that we’ve saved a bunch of boilerplate code in which the data is set into the Views. Unfortunately, we still have to write a bunch of ViewHolders for different RecyclerViews. It also isn’t clear how you’d extend this should you have multiple view types. We can fix these problems.

It is common to have only one data object passed into a data binding class, like item above. When you have this pattern, you can use naming convention to make a single ViewHolder for all RecyclerViews and all view types. The convention we’ll use is to name the one view model object “obj.” You may prefer “item” or “data,” but if I use “obj,” it is easier to pick out in the example.

In MyViewHolder, I am using ViewDataBinding, the base class for all generated bindings, instead of the specific ItemBinding. This way, I can support any layout in my ViewHolder. I’m also using setVariable() instead of the type-safe, but class-specific, setObj() method so that I can assign whatever view model object type that I need. The important part is that the variable must be named “obj” because I use BR.obj as the key in setVariable(). That means you must have a variable tag in your layout file like this:

Of course, your variable will have whatever type your data bound layout requires instead of “Item.”

I can then create a base class that can be used for all of my RecyclerView Adapters.

In this Adapter, the layout ID is being used as the view type so that it is easier to inflate the right binding. This lets the Adapter handle any number of layouts, but the most common usage is to have a RecyclerView with a single layout, so we can make a base class for that:

What’s Left?

All the boilerplate from the RecyclerView is now handled and all you have left to do is the hard part: loading data off the UI thread, notifying the adapter when there is a data change, etc. Android Data Binding only reduces the boring part.

Читайте также:  Камера с эффектами для android

You can also extend this technique to support multiple variables. It is common to supply an event handler object to handle things like click events and you may want to pass that along with a view model class. If you always pass in the Activity or Fragment, you could add those variables. As long as you use consistent naming, you can use this technique with all of your RecyclerViews.

Using Android Data Binding with RecyclerViews is easy and significantly reduces boilerplate code. Maybe your application will only need one ViewHolder and you’ll never need to write onCreateViewHolder() or onBindViewHolder() again!

Источник

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-файле.

Читайте также:  Документ microsoft word для андроид

Библиотека очень упрощает работу с 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.

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

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

Источник

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, реализация списков становиться совершенно обыденной вещью. Даже не нужно писать реализацию показанных выше вещей, а просто импортировав готовую библиотеку автора, или просто «скопипастив» их оттуда.

Источник

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