Пишем ObservableViewModel для биндинга в Android XML layout
Для чего нужно? В ViewModel получаем, подготавливаем к выводу нужные данные, а в xml просто используем/отображаем. Второй подход - создать много переменных ObservableField в ViewModel, но если их много то это не очень удобно.
Если заинтересовала тема DataBinding, то можно почитать это https://developer.android.com/topic/libraries/data-binding/index.html
C ним можно сильно упростить себе жизнь. Начиная от отказа от findViewById и заканчивая кастомными BindingAdapter для валидации текста, для загрузки картинок в ImageView без единой строчки java, обработка нажатий на кнопки с помощью переиспользуемых Action ( https://habrahabr.ru/post/305916/ ) и т.д., насколько хватит фантазии.
Базовый класс который реализует механизм уведомления разметки об изменениях данных:
Если заинтересовала тема DataBinding, то можно почитать это https://developer.android.com/topic/libraries/data-binding/index.html
C ним можно сильно упростить себе жизнь. Начиная от отказа от findViewById и заканчивая кастомными BindingAdapter для валидации текста, для загрузки картинок в ImageView без единой строчки java, обработка нажатий на кнопки с помощью переиспользуемых Action ( https://habrahabr.ru/post/305916/ ) и т.д., насколько хватит фантазии.
Базовый класс который реализует механизм уведомления разметки об изменениях данных:
- import android.content.Context;
- import android.databinding.Bindable;
- import android.databinding.Observable;
- import android.databinding.PropertyChangeRegistry;
- public class ObservableViewModel implements Observable {
- private transient PropertyChangeRegistry mCallbacks;
- public ObservableViewModel() {
- // ...
- }
- @Override
- public void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
- synchronized (this) {
- if (mCallbacks == null) {
- mCallbacks = new PropertyChangeRegistry();
- }
- }
- mCallbacks.add(callback);
- }
- @Override
- public void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
- synchronized (this) {
- if (mCallbacks == null) {
- return;
- }
- }
- mCallbacks.remove(callback);
- }
- public void notifyChange() {
- synchronized (this) {
- if (mCallbacks == null) {
- return;
- }
- }
- mCallbacks.notifyCallbacks(this, 0, null);
- }
- public void notifyPropertyChanged(int fieldId) {
- synchronized (this) {
- if (mCallbacks == null) {
- return;
- }
- }
- mCallbacks.notifyCallbacks(this, fieldId, null);
- }
- }
Пример имплементации
- public class ExampleViewModel extends ObservableViewModel {
- @Bindable
- public List<ExampleObject> getExampleObjects() {
- // Тут могут быть сложные вычисления, а не обычный геттер - результат кeшируется
- }
- public void onSomeDataChanged() {
- // Уведомляем что что-то изменилось, данные обновятся в xml
- // Переменная BR.exampleObjects генерируется автоматом
- notifyPropertyChanged(BR.exampleObjects);
- // Если нужно оповестить что поменялось все в ViewModel, то просто вызывыаем notifyChange();
- }
- }
Разметка
- <?xml version="1.0" encoding="utf-8"?>
- <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>
- <variable
- name="viewModel"
- type="com.example.ExampleViewModel"/>
- </data>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{viewModel.exampleObjects == 1 ? viewModel.exampleObjects.get(0) : @plurals/example_plurals_objects_count(viewModel.exampleObjects.size(), viewModel.exampleObjects.size())}"/>
- </layout>
Использовать в xml поле exampleObjects можно сколько угодно раз и как угодно, при биндинге это поле просчитывается и далее просто подставляется, а если обращаться как viewModel.getExampleObjects(), то будет просто вызываться метод.