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

View Binding — это инструмент, который позволяет проще писать код для взаимодейтсвия с view. При включении View Binding в определенном модуле он генерирует binding классы для каждого файла разметки (layout) в модуле. Объект сгенерированного binding класса содержит ссылки на все view из файла разметки, для которых указан android:id .
Как включить
Чтобы включить View Binding в модуле надо добавить элемент в файл build.gradle :
android < . viewBinding < enabled = true >>
Также можно указать, что для определенного файла разметки генерировать binding класс не надо. Для этого надо указать аттрибут tools:viewBindingIgnore=»true» в корневой view в нужном файле разметки.
Как использовать
Каждый сгенерированный binding класс содержит ссылку на корневой view разметки ( root ) и ссылки на все view, которые имеют id. Имя генерируемого класса формируется как «название файла разметки», переведенное в camel case + «Binding».
Например, для файла разметки result_profile.xml :
Будет сгенерирован класс ResultProfileBinding , содержащий 2 поля: TextView name и Button button . Для ImageView ничего сгенерировано не будет, как как оно не имеет id . Также в классе ResultProfileBinding будет метод getRoot() , возвращающий корневой LinearLayout .
Чтобы создать объект класса ResultProfileBinding , надо вызвать статический метод inflate() . После этого можно использовать корневой view как content view в Activity :
private lateinit var binding: ResultProfileBinding @Override fun onCreate(savedInstanceState: Bundle)
Позже binding можно использовать для получения view:
binding.name.text = viewModel.name binding.button.setOnClickListener
Отличия от других подходов
Главные преимущества View Binding — это Null safety и Type safety.
При этом, если какая-то view имеется в одной конфигурации разметки, но отсутствует в другой ( layout-land , например), то для нее в binding классе будет сгенерировано @Nullable поле.
Также, если в разных конфигурациях разметки имеются view с одинаковыми id, но разными типами, то для них будет сгенерировано поле с типом android.view.View .
(По крайней мере, в версии 3.6 Canary 11)
А вообще, было бы удобно, если бы сгенерированное поле имело наиболее возможный специфичный тип. Например, чтобы для Button в одной конфигурации и TextView в другой генерировалось поле с типом TextView ( public class Button extends TextView ).
При использовании View Binding все несоответствия между разметкой и кодом будут выявляться на этапе компиляции, что позволит избежать ненужных ошибок во время работы приложения.
Использование в RecyclerView.ViewHolder
Ничего не мешает использовать View Binding при создании view для RecyclerView.ViewHolder :
class PersonViewHolder(private val itemPersonBinding: ItemPersonBinding) : RecyclerView.ViewHolder(itemPersonBinding.root) < fun bind(person: Person) < itemPersonBinding.name.text = person.name >>
Однако, для создания такого ViewHolder придется написать немного бойлерплейта:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PersonViewHolder
Было бы удобнее, если при работе с RecyclerView.ViewHolder метод inflate(. ) не будет иметь параметр layoutInflater , а будет сам получать его из передаваемого parent .
Тут нужно ещё упомянуть, что при использовании View Binding поиск view через findViewById() производится только один раз при вызове метода inflate() . Это дает преимущество над kotlin-android-extensions , в котором кеширование view по умолчанию работало только в Activity и Fragment , а для RecyclerView.ViewHolder требовалась дополнительная настройка.
В целом, View Binding это очень удобная вещь, которую легко начать использовать в существующих проектах. Создатель Butter Knife уже рекомендует переключаться на View Binding.
Немного жаль, что такой инструмент не появился несколько лет назад.
- android
- android development
- transition
Урок 8. Android Data Binding – основы
Продолжаем курс по обучению основам разработки мобильных приложений в Android Studio на языке Kotlin. В этом уроке познакомимся с Android Data Binding.
- Data Binding Library
- С чего начать
- Что нам понадобится
- Подключаем Data Binding в проект
- Конвертируем макет в Data Binding
- Обернем макет в тег
- Layout variables и layout expressions
- Создадим layout expression
- Изменим Activity
Data Binding Library
Библиотека Data Binding Library, которая является частью Android Jetpack, позволяет привязывать компоненты пользовательского интерфейса в макетах к источникам данных в приложении, используя декларативный формат, а не программно. Другими словами, Data Binding поможет организовать работу с View так, чтобы нам не пришлось писать кучу методов findViewById, setText, setOnClickListener и т.п.
Чтобы более тесно на практике познакомиться с чистой архитектурой и архитектурными компонентами, записывайтесь на продвинутый курс по разработке приложения «Чат-мессенжер»
В этом цикле уроков вы узнаете, как настроить Data Binding в проекте, что такое layout expressions, как работать с observable objects и как создавать кастомные Binding Adapters чтобы свести избыточность (boilerplate) вашего кода к минимуму.
С чего начать
В этом уроке, мы возьмем уже существующий проект и конвертируем его в Data Binding:

Приложение имеет один экран, который показывает некоторые статические данные и некоторые наблюдаемые данные, что означает, что при изменении данных пользовательский интерфейс будет автоматически обновляться. Данные предоставлены ViewModel. Model-View-ViewModel – это шаблон уровня представления, который очень хорошо работает с Data Binding. Вот диаграмма паттерна MVVM:

Если вы еще не знакомы с классом ViewModel из библиотек компонентов архитектуры, вы можете посмотреть официальную документацию. Мы знакомились с этим классом на прошлом уроке, ссылку на который вы видите в правом верхнем углу этого видео. Это класс, который предоставляет состояние пользовательского интерфейса для представления (Activity, Fragment, т.п.). Он выдерживает изменения ориентации и действует как интерфейс для остальных слоев вашего приложения.
Что нам понадобится
- Среда разработки Android Studio 3.4 или более новой версии.
- Приложение без Data Binding
На этом этапе мы загрузим и запустим простое приложение-пример. Выполните в консоли команду:
$ git clone https://github.com/androidstart/DataBindingSample/tree/master
Вы также можете клонировать или скачать репозиторий в виде Zip-файла по ссылке на GitHub
- Распакуйте проект
- Откройте проект в Android Studio версии 3.4 или выше.
- Запустите приложение
Экран по умолчанию открывается и выглядит так:

Этот экран отображает несколько различных полей и кнопку, по нажатию которой можно увеличить счетчик, обновить индикатор выполнения и изображение. Логика действий, происходящих на экране, описана в классе SimpleViewModel. Откройте его и посмотрите.
Используйте Ctrl + N, чтобы быстро найти класс в Android Studio. Используйте Ctrl + Shift + N, чтобы найти файл по его имени.
На Mac найдите класс с помощью Command + O и файл с помощью Command + Shift + O.
В классе SimpleViewModel описаны такие поля:
- Имя и фамилия
- Количество лайков
- Уровень популярности
Кроме того, он позволяет пользователю увеличивать количество лайков методом onLike().
Пока SimpleViewModel содержит не самый интересный функционал, но здесь все в порядке. С другой стороны, класс главного экрана MainActivity имеет ряд проблем:
- Он вызывает метод findViewById несколько раз. Это не только медленно, но и небезопасно, потому что не проверяется на ошибки времени компиляции. Если идентификатор, который вы передаете в findViewById, неправильный, приложение аварийно завершит работу во время выполнения.
- Устанавливает начальные значения в onCreate. Было бы намного лучше иметь значения по умолчанию, которые устанавливаются автоматически.
- Использует в макете атрибут android:onClick который также не является безопасным: если метод onLike не реализован в активити (или переименован), приложение упадет в рантайме.
- В нем много кода. Активити и фрагменты имеют тенденцию расти очень быстро, поэтому желателно удалить из них как можно больше кода. Кроме того, код в активити и фрагментах трудно тестировать и поддерживать.
В нашей серии уроков с помощью библиотеки Data Binding мы собираемся исправить все эти проблемы, переместив логику из активити в места, где ее можно повторно использовать и проще тестировать.
Подключаем Data Binding в проект
Первым шагом является включение библиотеки Data Binding в модули, которые будут ее использовать. Добавьте такие строки в файл сборки модуля app:
android < . dataBinding < enabled true >>
Конвертируем макет в Data Binding
Откройте файл activity_main.xml. Это обычный макет с Constraint Layout в качестве корневого элемента.
Для того, чтобы преобразовать макет в Data Binding, необходимо:
- Обернуть ваш макет тегом
- Добавить переменные макета (layoutvariables)
- Добавить выражения макета (layoutexpressions)
Обернем макет в тег
Обернем корневой элемент в тег . Нам также придется переместить определения пространства имен (атрибуты, которые начинаются с xmlns:) в новый корневой элемент.
Очень удобно, что Android Studio предлагает способ сделать это автоматически:

Теперь ваш макет должен выглядеть так:
Между тегов мы разместим layout variables.
Layout variables и layout expressions
Переменные макета layout variables используются для записи выражений макета layout expressions. Layout expressions помещаются в значение атрибутов элемента. Для них используют формат записи @ .
Вот несколько примеров:
// Some examples of complex layout expressions android:text="@" android:visibility="@" android:transitionName='@'
Язык layout expressions довольно мощный, но вы должны использовать его только для базовой разметки, потому что более сложные выражения затруднят чтение и поддержку.
// Bind the name property of the viewmodel to the text attribute android:text="@" // Bind the nameVisible property of the viewmodel to the visibility attribute android:visibility="@" // Call the onLike() method on the viewmodel when the View is clicked. android:onClick="@ viewmodel.onLike()>"
С полным описанием языка можно ознакомиться здесь.
Теперь давайте привяжем некоторые данные.
Создадим layout expression
- Начнем с привязки статических данных. Создадим две String layout variables внутри тега .
- Найдем TextView по ID plain_name и добавим атрибут android:text с layout expression:
Выражения макета начинаются с символа @ и заключены в фигурные скобки < >.
Поскольку переменная name имеет тип String, Data Binding будет знать, как установить это значение в TextView. Позже вы узнаете, как обращаться с различными типами и атрибутами выражений макета.
- Сделайте то же самое с текстовым полем plain_lastName:
Теперь нам нужно изменить Activity, чтобы она правильно наполняла макетData Binding.
Изменим Activity
Макет готов, но нам нужно внести некоторые изменения в активити. Откроем MainActivity .
Поскольку мы используем Data Binding layout, наполнение макета происходит другим путем.
В onCreate, замените код определения макета разметки:
setContentView(R.layout.activity_main)
val binding : ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
Почему мы создаем переменную? Потому что нам нужен способ установить переменные макета, которые мы объявили в нашем блоке . Вот для чего предназначен binding object. Связывающие классы создаются библиотекой автоматически. Откройте сгенерированный класс ActivityMainBinding и посмотрите его код, он не сложен. Здесь поля, которые соответствуют элементам разметки, конструкторы, методы доступа, а также методы, которые инфлейтят и привязывают view-компоненты. Имя этого класса формируется автоматически из имени layout файла разметки (т.е. activity_main), плюс слово Binding. ActivityMainBinding все знает о нашем layout: какие View там есть, какие переменные (variable) мы там указывали, и как все это связать друг с другом, чтобы данные из переменных попадали во View.
- Теперь нам просто нужно установить значения переменных:
binding.name = "Your name" binding.lastName = "Your last name"
И это все. Мы просто связали данные с помощью библиотеки.
Теперь мы можем удалить ненужный код:
- Удалите метод updateName(), Data Binding автоматически находит поля по идентификаторам и устанавливает значения.
- Удалите вызов метода updateName() из onCreate().
Теперь вы можете запустить приложение. Имя и фамилия должны измениться.
Возможно, использование биндинга для пары TextView кажется бессмысленным. Но когда таких TextView десятки, то биндинг может избавить вас от написания кучи кода. Кроме того, мы рассмотрели совсем простой случай использования биндинга. На самом деле его возможности гораздо шире. Эти возможности мы рассмотрим в следующих уроках, где продолжим совершенствовать данный проект.
Исходный код измененного проекта здесь
Урок 18. Android Data Binding. Основы
В build.gradle файл модуля в секции android необходимо включить Data Binding:
android < . dataBinding < enabled = true >>
Правильно произносить байндинг , но биндинг звучит проще — буду использовать его.
Data Binding поможет организовать работу с View так, чтобы нам не пришлось писать кучу методов findViewById, setText, setOnClickListener и т.п. Давайте рассмотрим простой пример.
Есть класс Employee, который содержит в себе данные о работнике
public class Employee < public Employee(long id, String name, String address) < this.id = id; this.name = name; this.address = address; >public long id; public String name; public String address; >
поля: id, имя и адрес.
Мы хотим вывести имя и адрес работника на экран main_activity.xml:
Несложная задача. Для этого мы обычно пишем методы findViewById и setText. Тут все понятно.
Давайте рассмотрим, как это же можно сделать с помощью Data Binding.
Вносим изменения в main_activity.xml:
Корневым элементом теперь является , а LinearLayout сместился внутрь него.
В секции data мы объявляем переменную с именем employee. Ее тип — ранее рассмотренный класс Employee. Теперь мы можем использовать эту переменную в атрибутах вьюшек этого layout. В первом TextView, в атрибуте text мы используем employee.name, а в втором TextView — employee.address.
Обратите внимание, что мы не указываем id для View. В этом нет необходимости.
Как вы понимаете, нам остается лишь передать объект Employee в этот layout. И значения этого объекта будут подставлены в соответствующие TextView.
Это делается следующим образом.
public class MainActivity extends AppCompatActivity < @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); Employee employee = new Employee(1, "John Smith", "London"); MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity); binding.setEmployee(employee); >>
Сначала создаем Employee объект.
Затем используем DataBindingUtil. Метод DataBindingUtil.setContentView внутри себя сделает привычный нам setContentView для Activity, а также настроит и вернет объект биндинга MainActivityBinding.
MainActivityBinding — это сгенерированный класс. Имя этого класса берется из имени layout файла (т.е. main_activity), плюс слово Binding. MainActivityBinding все знает о нашем layout: какие View там есть, какие переменные (variable) мы там указывали, и как все это связать друг с другом, чтобы данные из переменных попадали в View.
Метод setEmployee был сгенерирован в классе биндинга, т.к. мы описали переменную employee в layout файле. Этим методом мы передаем биндингу объект Employee. Биндинг возьмет значения employee.name и employee.address и поместит их (методом setText) в соответствующие TextView. Все, как мы и настраивали в layout.

Данные из Employee помещены в TextView.
Сразу хочу заметить, что если мы теперь в коде будем изменять объект Employee, то данные на экране меняться не будут. Они считались один раз и далее не отслеживаются (при такой реализации).
Чтобы экран получил новые данные, надо снова передать биндингу измененный объект Employee:
binding.setEmployee(employee);
Или можно вызывать метод invalidateAll:
binding.invalidateAll();
Биндинг считает новые данные с ранее полученного объекта Employee.
Поля в Employee в этом примере я сделал public. Но вы можете сделать их private и создать для них public get методы.
Возможно, использование биндинга для пары TextView кажется бессмысленным. Но когда таких TextView десятки, то биндинг может избавить вас от написания кучи кода.
Кроме того, мы рассмотрели совсем простой случай использования биндинга. Но его возможности гораздо шире. Например, можно сделать так, чтобы при передаче в атрибут ImageView ссылки на картинку, биндинг запускал Picasso для загрузки этой картинки и помещал результат в ImageView. Или биндинг может сам отслеживать изменения в объекте с данными (в примере выше — это Employee ) и обновлять экран без каких-либо дополнительных методов.
Эти возможности мы рассмотрим в следующих уроках.
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
Что такое Binding и для чего он используется?
View Binding в данном контексте имеет смысл связывания XML элементов layout с атрибутом id в соответствующие View объекты.
В вашем случае, технически это выглядит как генерация класса ActivityPasswordlessBinding , который будет соответствовать вашему layout в сгенерированном классе будут поля именами совпадающими с вашими id задекларированным в layout .
В частности запись: binding.progressBar означает, что в вашем layout видимо есть ProgressBar с идентификатором progressBar
Вопреки комментариям, не имеет прямого отношения к архитектуре ViewModel. ViewModel прекрасно работает и без этого сахара. По сути это разновидность синтаксического сахара (ну чуть-чуть больше чем синтаксический сахар).
До появления view binding приходилось использовать различные ухищрения вроде аннотаций ButterKnife. Сейчас уже рекомендовано с ButterKnife пересаживаться на view binding:
Attention: This tool is now deprecated. Please switch to view binding. Existing versions will continue to work, obviously, but only critical bug fixes for integration with AGP will be considered. Feature development and general bug fixes have stopped.