При односторонней привязке данных вы можете задать значение атрибута и настроить прослушиватель, который будет реагировать на изменение этого атрибута:
<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, как показано в следующем фрагменте кода:
Kotlin
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) } } }
Java
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, выполните следующие действия:
Добавьте аннотацию к методу, который задает начальное значение и обновляет его при изменении, используя
@BindingAdapter:Kotlin
@BindingAdapter("time") @JvmStatic fun setTime(view: MyView, newValue: Time) { // Important to break potential infinite loops. if (view.time != newValue) { view.time = newValue } }
Java
@BindingAdapter("time") public static void setTime(MyView view, Time newValue) { // Important to break potential infinite loops. if (view.time != newValue) { view.time = newValue; } }
Добавьте аннотацию к методу, который считывает значение из представления, используя
@InverseBindingAdapter:Kotlin
@InverseBindingAdapter("time") @JvmStatic fun getTime(view: MyView) : Time { return view.getTime() }
Java
@InverseBindingAdapter("time") public static Time getTime(MyView view) { return view.getTime(); }
Теперь привязка данных знает, что делать при изменении данных (вызывать метод, аннотированный с помощью @BindingAdapter) и что вызывать при изменении атрибута представления (вызывать InverseBindingListener). Однако она не знает, когда и как изменяется атрибут.
Для этого нужно настроить прослушиватель в представлении. Это может быть специальный прослушиватель, связанный с вашим представлением, или общее событие, например потеря фокуса или изменение текста. Добавьте аннотацию @BindingAdapter к методу, который задает прослушиватель изменений свойства:
Kotlin
@BindingAdapter("app:timeAttrChanged") @JvmStatic fun setListeners( view: MyView, attrChange: InverseBindingListener ) { // Set a listener for click, focus, touch, etc. }
Java
@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 к одному из конвертеров и указать в ней ссылку на обратный конвертер. Пример такой конфигурации приведен в следующем фрагменте кода:
Kotlin
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. } }
Java
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:selectedItemPositionandroid:selection |
AdapterViewBindingAdapter
|
CalendarView |
android:date |
CalendarViewBindingAdapter
|
CompoundButton |
android:checked
|
CompoundButtonBindingAdapter
|
DatePicker
|
android:yearandroid:monthandroid: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:hourandroid:minute |
TimePickerBindingAdapter
|
Дополнительные ресурсы
Чтобы узнать больше о привязке данных, ознакомьтесь со следующими дополнительными ресурсами.
Образцы
Практические работы
Записи в блоге
Рекомендуем
- Примечание. Текст ссылки показывается, когда JavaScript отключен.
- Как работать с объектами наблюдаемых данных
- Макеты и выражения привязки
- Как связать макеты с компонентами архитектуры