В этом уроке рассматриваем возможность написания кода в layout и получаем View от биндинга. 

 


Полный список уроков курса:


 

Продолжаем говорить про DataBinding. Мы уже рассмотрели, как можно помещать значения из объектов в TextView. Но биндинг этим не ограничивается и дает нам возможность писать код прямо в layout.

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

 

Есть класс Employee:

public class Employee {

   public Employee(long id, String name, String address, int salary) {
       this.id = id;
       this.name = name;
       this.address = address;
       this.salary = salary;
   }

   public long id;

   public String name;

   public String address;

   public int salary;
}

Мы хотим выводить на экран имя, адрес и зарплату.

 

Экран будет таким: 

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

   <data>

       <variable
           name="employee"
           type="ru.startandroid.application.data.Employee" />

   </data>

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="vertical">

       <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{employee.name}" />

       <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{employee.address}" />

       <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{employee.salary}" />

   </LinearLayout>

</layout>

 

А вызов биндинга таким:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);

   Employee employee = new Employee(1, "John Smith", "London", 10000);

   MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
   binding.setEmployee(employee);
}

Ничего необычного. Все так же, как и в прошлом уроке.

 

Но при запуске получим ошибку: android.content.res.Resources$NotFoundException: String resource ID #0x2710

Так произошло, потому что биндинг попытался отобразить поле salary в TextView. Он просто выполнил код setText(employee.salary). И т.к. salary у нас имеет тип int, то TextView решил, что ему передают идентификатор строкового ресурса. И, конечно, он не нашел такую строку в strings.xml.

Это довольно часто возникающая ошибка. И в коде мы обычно решаем ее с помощью String.valueOf():

textView.setText(String.valueOf(employee.salary));

 

Биндинг позволяет сделать нам то же самое прямо в layout:

<TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="@{String.valueOf(employee.salary)}" />

Т.е. внутри @{ ... } мы можем писать простейший код и он будет выполнен.

 

Запустив приложение, мы увидим зарплату в TextView.

 

 

Рассмотрим еще несколько примеров:


Отображение в одном TextView сразу двух полей Employee

<TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text='@{employee.name + ", " + employee.address}' />

Обратите внимание на кавычки. Т.к. нам нужны двойные кавычки, чтобы добавить запятую между name и address, то весь этот код мы помещаем в одинарные кавычки.

 

 

Видимость View в зависимости от значения поля

<TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="@{employee.address}"
   android:visibility="@{TextUtils.isEmpty(employee.address) ? View.GONE : View.VISIBLE}"/>

Адрес будет отображен, только если он не пустой. А при пустом адресе видимость этого TextView будет GONE.

 

Обратите внимание, что мы здесь используем классы TextUtils и View. Если сейчас попытаться запустить приложение, то мы получим следующую ошибку: Identifiers must have user defined types from the XML file. TextUtils is missing it

Биндинг говорит, что не знает ничего про TextUtils. Нам надо добавить его в import. Делается это в секции data.

<data>
   <import type="android.view.View"/>
   <import type="android.text.TextUtils"/>

   <variable .../>

</data>

Теперь биндинг знает, какие классы мы имеем ввиду

 

Т.е. это аналогично тому, как в java коде вы пишете:

import android.text.TextUtils;
import android.view.View;

и после этого можете использовать эти классы.

 

 

Использование resources значения: strings, dimens и пр.

<TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="@{TextUtils.isEmpty(employee.address) ? @string/empty_address : employee.address}"/>

Если адрес пустой, то показываем заглушку из strings.

 

 

В примерах выше мы использовали в layout только одну переменную - Employee. Давайте добавим еще одну.

Создадим новый класс, который будет содержать информацию об отделе

public class Department {

   public Department(long id, String name) {
       this.id = id;
       this.name = name;
   }

   public long id;

   public String name;
}

 

Добавим переменную типа Department в layout

<data>

   <variable
       name="employee"
       type="ru.startandroid.application.data.Employee" />

   <variable
       name="department"
       type="ru.startandroid.application.data.Department" />

</data>

 

И используем ее:

<TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text='@{employee.name + "(" + department.name + ")"}' />

В одном TextView показываем данные из двух переменных.

 

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

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);

   Employee employee = new Employee(1, "John Smith", "", 10000);
   Department department = new Department(100, "IT");

   MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
   binding.setEmployee(employee);
   binding.setDepartment(department);
}

Для переменной Department в классе MainActivityBinding был сгенерирован отдельный метод setDepartment.

 

Можно немного усложнить логику и показывать название отдела, только если мы передали объект Department в биндинг:

<TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text='@{employee.name + (department == null ? "" : " (" + department.name + ")") }' />

Показываем отдел, если department не null 

 

 

Биндинг умеет работать и с коллекциями. Например, если в Employee есть поле со списком хобби:

public List<String> hobbies; 

то, в layout мы можем отобразить первое хобби из списка следующим образом:

<TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="@{employee.hobbies[0]}" />

 

 

Если нам необходимо использовать список, как отдельную переменную в layout, то variable будет выглядеть так:

<variable
   name="hobbies"
   type="java.util.List&lt;String&gt;" />

Ограничения XML не позволяют просто так использовать символы < и >. Поэтому их приходится заменять спецсимволами &lt; и &gt;.

 

То же самое описание переменной, но List вынесен в импорт:

<import type="java.util.List"/>

<variable
   name="hobbies"
   type="List&lt;String&gt;" />

 

 

Map коллекции описываются аналогично:

<import type="java.util.Map"/>

<variable
   name="map"
   type="Map&lt;String, String&gt;"/>

 

Получение значения по ключу:

<TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="@{map[`key`]}" />

 


В официальной документации вы можете посмотреть полный список возможностей написания кода в layout.

 

 

 

View

Если нам нужны какие-либо View из нашего layout, то их можно получить из биндинга. Для этого необходимо, чтобы View имело id.

Например, если в layout есть поле:

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />


то мы можем получить его из биндинга так:

TextView textViewName = binding.name;

 

Также можно получить корневое View методом getRoot:

View rootView = binding.getRoot();

Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

- тут можно посмотреть, над какими уроками я сейчас работаю, и о чем будут следующие уроки 




Language

Социальные сети

 

Telegram канал



Android чат в Telegram



Группа ВКонтакте



Страница в Facebook