Двусторонняя привязка данных

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

 <CheckBox     android:id="@+id/rememberMeCheckBox"     android:checked="@{viewmodel.rememberMe}"     android:onCheckedChanged="@{viewmodel.rememberMeChanged}" /> 

Двусторонняя привязка данных упрощает этот процесс:

 <CheckBox     android:id="@+id/rememberMeCheckBox"     android:checked="@={viewmodel.rememberMe}" /> 

Нотация @={} , которая, что немаловажно, включает знак "=", позволяет получать изменения данных свойства и одновременно прослушивать обновления пользователя.

Чтобы реагировать на изменения в резервных данных, вы можете сделать переменную макета реализацией Observable , обычно BaseObservable , и использовать аннотацию @Bindable , как показано в следующем фрагменте кода:

Котлин

 class LoginViewModel : BaseObservable {     // val data = ...      @Bindable     fun getRememberMe(): Boolean {         return data.rememberMe     }      fun setRememberMe(value: Boolean) {         // Avoids infinite loops.         if (data.rememberMe != value) {             data.rememberMe = value              // React to the change.             saveData()              // Notify observers of a new value.             notifyPropertyChanged(BR.remember_me)         }     } } 

Ява

 public class LoginViewModel extends BaseObservable {     // private Model data = ...      @Bindable     public Boolean getRememberMe() {         return data.rememberMe;     }      public void setRememberMe(Boolean value) {         // Avoids infinite loops.         if (data.rememberMe != value) {             data.rememberMe = value;              // React to the change.             saveData();              // Notify observers of a new value.             notifyPropertyChanged(BR.remember_me);         }     } }

Поскольку метод получения привязываемого свойства называется getRememberMe() , соответствующий метод установки свойства автоматически использует имя setRememberMe() .

Дополнительные сведения об использовании BaseObservable и @Bindable см. в разделе Работа с наблюдаемыми объектами данных .

Двусторонняя привязка данных с использованием пользовательских атрибутов

Платформа предоставляет реализации двусторонней привязки данных для наиболее распространенных двусторонних атрибутов и прослушивателей изменений, которые вы можете использовать как часть своего приложения. Если вы хотите использовать двустороннюю привязку данных с настраиваемыми атрибутами, вам необходимо работать с аннотациями @InverseBindingAdapter и @InverseBindingMethod .

Например, если вы хотите включить двустороннюю привязку данных для атрибута "time" в пользовательском представлении под названием MyView , выполните следующие шаги:

  1. Аннотируйте метод, который устанавливает начальное значение и обновляет его при изменении значения, используя @BindingAdapter :

    Котлин

     @BindingAdapter("time") @JvmStatic fun setTime(view: MyView, newValue: Time) {     // Important to break potential infinite loops.     if (view.time != newValue) {         view.time = newValue     } }

    Ява

     @BindingAdapter("time") public static void setTime(MyView view, Time newValue) {     // Important to break potential infinite loops.     if (view.time != newValue) {         view.time = newValue;     } }
  2. Аннотируйте метод, который считывает значение из представления, используя @InverseBindingAdapter :

    Котлин

     @InverseBindingAdapter("time") @JvmStatic fun getTime(view: MyView) : Time {     return view.getTime() }

    Ява

     @InverseBindingAdapter("time") public static Time getTime(MyView view) {     return view.getTime(); }

На этом этапе привязка данных знает, что делать, когда данные изменяются (она вызывает метод, аннотированный @BindingAdapter ) и что вызывать, когда изменяется атрибут представления (она вызывает InverseBindingListener ). Однако он не знает, когда и как изменится атрибут.

Для этого вам нужно настроить прослушиватель для представления. Это может быть пользовательский прослушиватель, связанный с вашим пользовательским представлением, или это может быть общее событие, например потеря фокуса или изменение текста. Добавьте аннотацию @BindingAdapter к методу, который устанавливает прослушиватель изменений свойства:

Котлин

 @BindingAdapter("app:timeAttrChanged") @JvmStatic fun setListeners(         view: MyView,         attrChange: InverseBindingListener ) {     // Set a listener for click, focus, touch, etc. }

Ява

 @BindingAdapter("app:timeAttrChanged") public static void setListeners(         MyView view, final InverseBindingListener attrChange) {     // Set a listener for click, focus, touch, etc. } 

Прослушиватель включает InverseBindingListener в качестве параметра. Вы используете InverseBindingListener , чтобы сообщить системе привязки данных, что атрибут изменился. Затем система может начать вызывать метод, аннотированный с помощью @InverseBindingAdapter , и так далее.

На практике этот прослушиватель включает в себя некоторую нетривиальную логику, включая прослушиватели для односторонней привязки данных. Пример см. в адаптере для изменения текстового атрибута TextViewBindingAdapter .

Конвертеры

Если переменную, привязанную к объекту View , необходимо отформатировать, перевести или каким-либо образом изменить перед отображением, можно использовать объект Converter .

Например, возьмем объект EditText , отображающий дату:

<EditText     android:id="@+id/birth_date"     android:text="@={Converter.dateToString(viewmodel.birthDate)}" /> 

Атрибут viewmodel.birthDate содержит значение типа Long , поэтому его необходимо отформатировать с помощью конвертера.

Поскольку используется двустороннее выражение, также необходим обратный преобразователь , который позволит библиотеке знать, как преобразовать предоставленную пользователем строку обратно в резервный тип данных, в данном случае Long . Этот процесс выполняется путем добавления аннотации @InverseMethod к одному из преобразователей, и эта аннотация ссылается на обратный преобразователь. Пример этой конфигурации показан в следующем фрагменте кода:

Котлин

 object Converter {     @InverseMethod("stringToDate")     @JvmStatic fun dateToString(         view: EditText, oldValue: Long,         value: Long     ): String {         // Converts long to String.     }      @JvmStatic fun stringToDate(         view: EditText, oldValue: String,         value: String     ): Long {         // Converts String to long.     } }

Ява

 public class Converter {     @InverseMethod("stringToDate")     public static String dateToString(EditText view, long oldValue,             long value) {         // Converts long to String.     }      public static long stringToDate(EditText view, String oldValue,             String value) {         // Converts String to long.     } }

Бесконечные циклы с использованием двусторонней привязки данных

Будьте осторожны, чтобы не создавать бесконечные циклы при использовании двусторонней привязки данных. Когда пользователь изменяет атрибут, вызывается метод, аннотированный с помощью @InverseBindingAdapter , и значение присваивается резервному свойству. Это, в свою очередь, вызовет метод, аннотированный с помощью @BindingAdapter , что вызовет еще один вызов метода, аннотированного с помощью @InverseBindingAdapter , и так далее.

По этой причине важно разорвать возможные бесконечные циклы, сравнивая новые и старые значения в методах, аннотированных с помощью @BindingAdapter .

Двусторонние атрибуты

Платформа предоставляет встроенную поддержку двусторонней привязки данных при использовании атрибутов в следующей таблице. Подробности о том, как платформа обеспечивает такую ​​поддержку, см. в реализациях соответствующих адаптеров привязки:

Сорт Атрибут(ы) Привязка адаптера
AdapterView android:selectedItemPosition
android:selection
AdapterViewBindingAdapter
CalendarView android:date CalendarViewBindingAdapter
CompoundButton android:checked CompoundButtonBindingAdapter
DatePicker android:year
android:month
android:day
DatePickerBindingAdapter
NumberPicker android:value NumberPickerBindingAdapter
RadioButton android:checkedButton RadioGroupBindingAdapter
RatingBar android:rating RatingBarBindingAdapter
SeekBar android:progress SeekBarBindingAdapter
TabHost android:currentTab TabHostBindingAdapter
TextView android:text TextViewBindingAdapter
TimePicker android:hour
android:minute
TimePickerBindingAdapter

Дополнительные ресурсы

Чтобы узнать больше о привязке данных, обратитесь к следующим дополнительным ресурсам.

Образцы

Кодлабы

Сообщения в блоге

{% дословно %} {% дословно %} {% дословно %} {% дословно %}