Урок 8. Android Data Binding – основы

Продолжаем курс по обучению основам разработки мобильных приложений в Android Studio на языке Kotlin. В этом уроке познакомимся с Android Data Binding.

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:

Диаграмма паттерна диаграмма паттерна Model-View-ViewModel

Если вы еще не знакомы с классом ViewModel из библиотек компонентов архитектуры, вы можете посмотреть официальную документацию. Мы знакомились с этим классом на прошлом уроке, ссылку на который вы видите в правом верхнем углу этого видео. Это класс, который предоставляет состояние пользовательского интерфейса для представления (Activity, Fragment, т.п.). Он выдерживает изменения ориентации и действует как интерфейс для остальных слоев вашего приложения.

Что нам понадобится

  • Среда разработки Android Studio 3.4 или более новой версии.
  • Приложение без Data Binding

На этом этапе мы загрузим и запустим простое приложение-пример. Выполните в консоли команду:

$ git clone https://github.com/androidstart/DataBindingSample/tree/master

Вы также можете клонировать или скачать репозиторий в виде Zip-файла по ссылке на GitHub

  1. Распакуйте проект
  2. Откройте проект в Android Studio версии 3.4 или выше.
  3. Запустите приложение

Экран по умолчанию открывается и выглядит так:

Экран по умолчанию

Этот экран отображает несколько различных полей и кнопку, по нажатию которой можно увеличить счетчик, обновить индикатор выполнения и изображение. Логика действий, происходящих на экране, описана в классе 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:

build.gradle

android {
...
    dataBinding {
       enabled true
    }
}

 

Конвертируем макет в Data Binding

Откройте файл activity_main.xml. Это обычный макет с Constraint Layout в качестве корневого элемента.

Для того, чтобы преобразовать макет в Data Binding, необходимо:

  • Обернуть ваш макет тегом <layout>
  • Добавить переменные макета (layout variables)
  • Добавить выражения макета (layout expressions)

Обернем макет в тег  <layout>

Обернем корневой элемент в тег <layout>. Нам также придется переместить определения пространства имен (атрибуты, которые начинаются с xmlns:) в новый корневой элемент.

Очень удобно, что Android Studio предлагает способ сделать это автоматически:

Преобразование макета разметки в Data Binding layout средствами Android Studio

Теперь ваш макет должен выглядеть так:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:app="http://schemas.android.com/apk/res-auto"
       xmlns:tools="http://schemas.android.com/tools">
   <data>

   </data>
   <androidx.constraintlayout.widget.ConstraintLayout
           android:layout_width="match_parent"
           android:layout_height="match_parent">

       <TextView
...

 

Между тегов  <data> мы разместим layout variables.

Layout variables и layout expressions

Переменные макета layout variables используются для записи выражений макета layout expressions. Layout expressions помещаются в значение атрибутов элемента.  Для них используют формат записи @{expression} .

Вот несколько примеров:

// Some examples of complex layout expressions
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

 

Язык layout expressions довольно мощный, но вы должны использовать его только для базовой разметки, потому что более сложные выражения затруднят чтение и поддержку.

// Bind the name property of the viewmodel to the text attribute
android:text="@{viewmodel.name}"
// Bind the nameVisible property of the viewmodel to the visibility attribute
android:visibility="@{viewmodel.nameVisible}"
// Call the onLike() method on the viewmodel when the View is clicked.
android:onClick="@{() -> viewmodel.onLike()}"

 

С полным описанием языка можно ознакомиться здесь.

Теперь давайте привяжем некоторые данные.

Создадим layout expression

  • Начнем с привязки статических данных. Создадим две String layout variables внутри тега <data>.
    <data>
        <variable name="name" type="String"/>
        <variable name="lastName" type="String"/>
    </data>

 

  • Найдем TextView по ID plain_name и добавим атрибут android:text с layout expression:
 <TextView
                android:id="@+id/plain_name"
                android:text="@{name}" 
        ... />

 

Выражения макета начинаются с символа @ и заключены в фигурные скобки { }.

Поскольку переменная name имеет тип String, Data Binding будет знать, как установить это значение в TextView. Позже вы узнаете, как обращаться с различными типами и атрибутами выражений макета.

  • Сделайте то же самое с текстовым полем plain_lastName:
 <TextView
                android:id="@+id/plain_lastname"
                android:text="@{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)

Почему мы создаем переменную? Потому что нам нужен способ установить переменные макета, которые мы объявили в нашем блоке <data>. Вот для чего предназначен 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 десятки, то биндинг может избавить вас от написания кучи кода. Кроме того, мы рассмотрели совсем простой случай использования биндинга. На самом деле его возможности гораздо шире. Эти возможности мы рассмотрим в следующих уроках, где продолжим совершенствовать данный проект.

Исходный код измененного проекта здесь

Урок 9. Android Data Binding с событиями пользовательского интерфейса и наблюдаемыми данными

Коментарі: 4
  1. Lastandlost
    Lastandlost

    Не компилирует приложение выдает ошибки:

    e: java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
    Execution failed for task ‘:app:kaptDebugKotlin’.
    > Internal compiler error. See log for more details

    * Try:
    Run with —stacktrace option to get the stack trace. Run with —info or —debug option to get more log output. Run with —scan to get full insights.

    1. Lastandlost
      Lastandlost

      А при попытке просмотра кода ActivityMainBinding переправляет меня не в сгенерированный класс ActivityMainBinding (что-то мне подсказывает, что он попросту у меня отсутствует), а в activity_main.xml

  2. genbachae
    genbachae

    Несмотря на нижеизложенное различие приложение запустилось и работает!

  3. genbachae
    genbachae

    Виталий, у меня класс “ActivityMainBinding” сгенерировался следующим содержимым:
    package info.fandroid.databindingsample.ui

    import android.os.Parcel
    import android.os.Parcelable

    class ActivityMainBinding() : Parcelable {
    constructor(parcel: Parcel) : this() {
    }

    override fun writeToParcel(parcel: Parcel, flags: Int) {

    }

    override fun describeContents(): Int {
    return 0
    }

    companion object CREATOR : Parcelable.Creator {
    override fun createFromParcel(parcel: Parcel): ActivityMainBinding {
    return ActivityMainBinding(parcel)
    }

    override fun newArray(size: Int): Array {
    return arrayOfNulls(size)
    }
    }
    }
    это содержимое отличается от того содержимого что показано в вашем видео. Где я ошибся?

Додати коментар