В этом уроке разберем, как настроить автоматическую передачу данных в View и обратно.
Полный список уроков курса:
- Урок 1. Lifecycle
- Урок 2. LiveData
- Урок 3. LiveData. Дополнительные возможности
- Урок 4. ViewModel
- Урок 5. Room. Основы
- Урок 6. Room. Entity
- Урок 7. Room. Insert, Update, Delete, Transaction
- Урок 8. Room. Query
- Урок 9. Room. RxJava
- Урок 10. Room. Запрос из нескольких таблиц. Relation
- Урок 11. Room. Type converter
- Урок 12. Room. Миграция версий базы данных
- Урок 13. Room. Тестирование
- Урок 14. Paging Library. Основы
- Урок 15. Paging Library. PagedList и DataSource. Placeholders.
- Урок 16. Paging Library. LivePagedListBuilder. BoundaryCallback.
- Урок 17. Paging Library. Виды DataSource
- Урок 18. Android Data Binding. Основы
- Урок 19. Android Data Binding. Код в layout. Доступ к View
- Урок 20. Android Data Binding. Обработка событий
- Урок 21. Android Data Binding. Observable поля. Двусторонний биндинг.
- Урок 22. Android Data Binding. Adapter. Conversion.
- Урок 23. Android Data Binding. Использование с include, ViewStub и RecyclerView.
- Урок 24. Navigation Architecture Component. Введение
- Урок 25. Navigation. Передача данных. Type-safe аргументы.
- Урок 26. Navigation. Параметры навигации
- Урок 27. Navigation. NavigationUI.
- Урок 28. Navigation. Вложенный граф. Global Action. Deep Link.
- Урок 29. WorkManager. Введение
- Урок 30. WorkManager. Критерии запуска задачи.
- Урок 31. WorkManager. Последовательность выполнения задач.
- Урок 32. WorkManager. Передача и получение данных
- Урок 33. Практика. О чем это будет.
- Урок 34. Практика. TodoApp. Список задач.
- Урок 35. Практика. TodoApp. Просмотр задачи
Когда мы используем биндинг для обычного Java объекта, то экран не будет автоматически меняться при изменении значений в этом объекте.
Т.е. вы передаете объект Employee в биндинг, а затем можете сколько угодно менять в нем поля и ничего на экране меняться не будет. Вам надо будет снова руками передать измененный объект в биндинг или вызвать у биндинга метод invalidateAll, тогда экран отобразит актуальные данные.
В первом уроке по Data Binding я упоминал, что есть возможность сделать так, чтобы биндинг сам мониторил значения полей и обновлял экран, как только произошли какие-то изменения. Для этого надо использовать механизмы Observable.
Observable поля
Сделаем несколько Observable полей в классе Employee:
public class Employee { public Employee(long id, String name, String address, int salary) { this.id = id; this.name.set(name); this.address = address; this.salary.set(salary); } public long id; public ObservableField<String> name = new ObservableField<>(); public String address; public ObservableInt salary = new ObservableInt(); }
Для Java примитивов есть готовые Observable поля: ObservableInt, ObservableFloat и т.п. Для остальных используем ObservableField с указанием типа. Чтобы присвоить такому полю значение, используем метод set.
Поля name и salary делаем Observable. Их мы будем использовать в биндинге.
layout файл выглядит как обычно, в нем ничего менять не надо:
<?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="@{String.valueOf(employee.salary)}"/> </LinearLayout> </layout>
Теперь, передав в биндинг объект Employee, вы сможете менять значения его полей:
employee.name.set("Mark"); employee.salary.set(20000);
А биндинг сам отследит эти изменения и обновит экран.
Для коллекций есть классы ObservableArrayMap и ObservableArrayList.
Биндинг будет работать, даже если передавать значения в Observable поля не в UI потоке.
Рассмотрим еще один возможный сценарий использования ObservableField. Его можно использовать не только с отдельными полями объекта, но и с целым объектом.
Есть класс 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; }
Вполне может быть ситуация, когда нам в ViewModel (или в презентер) периодически "прилетает" из репозитория новый объект Employee и его надо отобразить в View.
В этом случае в ViewModel создаем поле ObservableField<Employee>:
public class ViewModel { public ObservableField<Employee> employee = new ObservableField<>(); ... }
В это поле методом employee.set() репозиторий будет помещать новый Employee.
В layout в качестве переменной используем ViewModel.
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="model" type="ru.startandroid.application.data.ViewModel"/> </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="@{model.employee.name}" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{model.employee.address}"/> </LinearLayout> </layout>
Достаем из model объект employee и используем его поля в биндинге.
Теперь при обновлении значения в ObservableField<Employee> будут изменены и поля в View без каких-либо дополнительных действий с нашей стороны.
BaseObservable
Есть еще один способ включить автобиндинг для Java объекта.
Делается это наследованием BaseObservable:
public class Employee extends BaseObservable { private long id; private String name = ""; private int salary = 0; public Employee(long id, String name, int salary) { this.id = id; setName(name); setSalary(salary); } @Bindable public String getName() { return name; } public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); } @Bindable public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; notifyPropertyChanged(BR.salary); } }
Поле id я оставил обычным. А поля name и salary будут отслеживаться биндингом. Для этого надо пометить get-методы аннотацией @Bindable, а в set-методах вызывать notifyPropertyChanged метод, который и будет уведомлять биндинг об изменениях значения поля.
В layout все будет как обычно.
Двусторонний биндинг
Биндинг может работать в обе стороны. Т.е. он будет не только передавать данные в View, но и получать их оттуда.
Рассмотрим на примере пары полей в Employee:
public class Employee { public String name = ""; public boolean enabled = true; }
Поле name и статус enabled. Настроим биндинг этих полей в EditText и CheckBox.
При этом сделаем так, чтобы биндинг работал в обе стороны. Для этого надо в строке биндинга добавить символ = между @ и {...}
<EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@={employee.name}"/> <CheckBox android:text="enabled" android:layout_width="wrap_content" android:layout_height="wrap_content" android:checked="@={employee.enabled}"/>
Теперь при изменении текста в EditText, биндинг будет передавать новое значение в employee.name. А при включении\выключении чекбокса, биндинг будет передавать текущее состояние этого чекбокса в поле employee.enabled.
Т.е. изменения содержимого View будут отражены в Employee объекте, который мы передавали в биндинг. Если необходимо, можно использовать и Observable поля. С ними это тоже будет работать.
Кстати, если после передачи в биндинг вы нигде не храните у себя объект employee, то вы всегда можете получить его обратно методом binding.getEmployee().
Присоединяйтесь к нам в Telegram:
- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня