March 18, 2020

Android Data Binding – кастомные атрибуты и Binding Adapters

Binding Adapters, создание пользовательских атрибутов

В предыдущей статье мы рассмотрели обработку пользовательских событий ввода и работу с наблюдаемыми данными (observable data), при изменении которых будет меняться их представление. В этой статье мы рассмотрим создание кастомных атрибутов, а также использование нескольких параметров для Binding Adapters.

Когда вы связываете строку (или наблюдаемую строку) с атрибутом android:text, становится совершенно очевидно, что произойдет, но каким образом это происходит?

Благодаря библиотеке Data Binding почти все вызовы пользовательского интерфейса выполняются в статических методах, называемых адаптерами привязки.

Библиотека предоставляет огромное количество связывающих адаптеров. Посмотрите их здесь . Вот пример для атрибута android:text:

@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
    // Some checks removed for clarity
    view.setText(text);
}

Или android:background:

@BindingAdapter("android:background")
public static void setBackground(View view, Drawable drawable) {
    if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
        view.setBackground(drawable);
    } else {
        view.setBackgroundDrawable(drawable);
    }
}

В привязке данных нет магии. Все решается во время компиляции и доступно для чтения в сгенерированном коде.

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

Мы хотим, чтобы выполнялись следующие условия:

  • Индикатор был невидимым, если нет лайков
  • Заполнялся после 5 лайков
  • Изменял цвет, если заполнен

Давайте создадим собственные адаптеры привязки для этого.

Создайте файл BindingAdapters.kt. Неважно, где вы создадите адаптеры, библиотека их найдет. В Kotlin статические методы можно создавать, добавляя функции на верхний уровень Котлин-файла или как функции расширения класса.

Создайте внутри адаптер связывания для первого условия hideIfZero:

@BindingAdapter("app:hideIfZero")
fun hideIfZero(view: View, number: Int) {
    view.visibility = if (number == 0) View.GONE else View.VISIBLE
}

Это обязательный адаптер, который:

  1. Относится к атрибуту app:hideIfZero.
  2. Может применяться к каждому представлению (поскольку первый параметр является представлением; вы можете ограничить его определенными классами, изменив этот тип).
  3. Принимает Integer , который возвращает выражение макета.
  4. Выполняет View GONE, если число равно нулю. В противном случае VISIBLE.

В макете разметки найдите индикатор выполнения и добавьте атрибут hideIfZero:

<ProgressBar
    android:id="@+id/progressBar"
    app:hideIfZero="@{viewmodel.likes}"
    ... />

Запустите приложение, и вы увидите, что индикатор выполнения отображается при первом нажатии кнопки. Однако нам все еще нужно изменить его значение и цвет:

Binding Adapters с несколькими параметрами

Для значения прогресса мы будем использовать адаптер привязки, который принимает максимальное значение и количество лайков. Откройте файл BindingAdapters и добавьте это:

@BindingAdapter(
    value = ["app:progressScaled", "android:max"],
    requireAll = true
)
fun setProgress(progressBar: ProgressBar, likes: Int, max: Int) {
    progressBar.progress = (likes * max / 5).coerceAtMost(max)
}

Этот адаптер привязки не используется, если какие-либо атрибуты отсутствуют. Это определяется во время компиляции. Теперь метод принимает 3 параметра (представление, к которому он применяется, + количество атрибутов, определенных в аннотации).

Параметр requireAll определяет, когда используется связывающий адаптер:

  • Если true, все элементы должны присутствовать в определении XML.
  • Если false, отсутствующие атрибуты будут иметь значение null, false (если логическое значение) или 0 (если примитивы).

Теперь добавьте атрибуты в XML:

<ProgressBar
    android:id="@+id/progressBar"
    app:hideIfZero="@{viewmodel.likes}"
    app:progressScaled="@{viewmodel.likes}"
    android:max="@{100}"
    .../>

Мы привязываем атрибут progressScaled к количеству лайков и просто передаем целое число в атрибут max. Если вы не добавите формат @{}, привязка данных не сможет найти правильный адаптер привязки.

Если вы запустите приложение, вы увидите, как индикатор выполнения заполняется, как и ожидалось.

Практика создания Binding Adapters

Практика помогает закрепить материал. В качестве домашнего задания попробуйте создать:

  1. Адаптер связывания, который окрашивает цвет индикатора выполнения в зависимости от значения лайков и добавляет соответствующий атрибут.
  2. Адаптер связывания, который показывает различный значок в зависимости от популярности:
  • ic_person_black_96dp в черном цвете,
  • ic_whatshot_black_96dp в светло-розовом цвете,
  • ic_whatshot_black_96dp в насыщенном розовом цвете.

На этом все. Исходный код проекта можно скачать здесь.

Источник: Урок 10. Android Data Binding. Binding Adapters