Con la vinculación de datos unidireccional, puedes establecer un valor en un atributo y un objeto de escucha que reaccione a un cambio en ese atributo:
<CheckBox android:id="@+id/rememberMeCheckBox" android:checked="@{viewmodel.rememberMe}" android:onCheckedChanged="@{viewmodel.rememberMeChanged}" />
La vinculación de datos bidireccional proporciona un acceso directo a este proceso:
<CheckBox android:id="@+id/rememberMeCheckBox" android:checked="@={viewmodel.rememberMe}" />
La notación @={}
, que incluye el signo "=", recibe cambios en los datos de la propiedad y escucha las actualizaciones de los usuarios al mismo tiempo.
Para reaccionar a los cambios en los datos de copia de seguridad, puedes hacer que tu variable de diseño sea una implementación de Observable
, generalmente BaseObservable
, y usar un @Bindable
, como se muestra en el siguiente fragmento de código:
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); } } }
Debido a que el método get de la propiedad vinculante se llama getRememberMe()
, el método set correspondiente de la propiedad usa automáticamente el nombre setRememberMe()
.
Para obtener más información sobre cómo usar BaseObservable
y @Bindable
, consulta Cómo trabajar con objetos de datos observables.
Vinculación de datos bidireccional con atributos personalizados
La plataforma proporciona implementaciones de vinculación de datos bidireccional para los atributos bidireccionales más comunes y objetos de escucha de cambio, que puedes usar como parte de la app. Si deseas utilizar la vinculación de datos bidireccional con atributos personalizados, debes trabajar con las anotaciones @InverseBindingAdapter
y @InverseBindingMethod
.
Por ejemplo, si deseas habilitar la vinculación de datos bidireccional en un atributo "time"
, en una vista personalizada llamada MyView
, sigue estos pasos:
Anota el método que establece el valor inicial y se actualiza cuando el valor cambia con
@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; } }
Anota el método que lee el valor de la vista con
@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(); }
En este punto, la vinculación de datos sabe qué hacer cuando los datos cambian (llama al método anotado con @BindingAdapter
) y a qué llamar cuando cambia el atributo de vista (llama al InverseBindingListener
). Sin embargo, no sabe cuándo ni cómo cambia el atributo.
Para eso, necesitas establecer un objeto de escucha en la vista. Puede ser un objeto de escucha personalizado asociado con la vista personalizada o puede ser un evento genérico, como una pérdida de enfoque o un cambio de texto. Agrega la anotación @BindingAdapter
al método que establece el objeto de escucha para los cambios en la propiedad:
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. }
El objeto de escucha incluye un InverseBindingListener
como parámetro. Usa InverseBindingListener
para indicarle al sistema de vinculación de datos que el atributo cambió. Luego, el sistema puede comenzar a llamar al método anotado usando @InverseBindingAdapter
, y así sucesivamente.
En la práctica, este objeto de escucha incluye alguna lógica no trivial, incluidos los objetos de escucha para la vinculación de datos unidireccional. Si quieres ver un ejemplo, consulta el adaptador para el cambio de atributo de texto, TextViewBindingAdapter
.
Converters
Si la variable que está vinculada a un objeto View
debe formatearse, traducirse o modificarse de alguna manera antes de mostrarse, es posible usar un objeto Converter
.
Por ejemplo, toma un objeto EditText
que muestra una fecha:
<EditText
android:id="@+id/birth_date"
android:text="@={Converter.dateToString(viewmodel.birthDate)}"
/>
El atributo viewmodel.birthDate
contiene un valor de tipo Long
, por lo que debe formatearse con un convertidor.
Debido a que se usa una expresión bidireccional, también debe haber una convertidor inverso para que la biblioteca sepa cómo convertir la string proporcionada por el usuario de datos de copia de seguridad (en este caso, Long
). Este proceso se realiza agregando la anotación @InverseMethod
a uno de los convertidores y haciendo que esta anotación haga referencia al convertidor inverso. En el siguiente fragmento de código, se incluye un ejemplo de este proceso:
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. } }
Bucles infinitos con vinculación de datos bidireccional
Ten cuidado de no introducir bucles infinitos cuando uses la vinculación de datos bidireccional. Cuando el usuario cambia un atributo, se llama al método anotado usando @InverseBindingAdapter
y el valor se asigna a la propiedad de copia de seguridad. Esto, a su vez, llamaría al método anotado usando @BindingAdapter
, lo que activaría otra llamada al método anotado con @InverseBindingAdapter
, y así sucesivamente.
Por esa razón, es importante romper posibles bucles infinitos comparando valores nuevos y antiguos en los métodos anotados con @BindingAdapter
.
Atributos bidireccionales
La plataforma proporciona compatibilidad integrada para la vinculación de datos bidireccional cuando utilizas los atributos de la siguiente tabla. Para obtener detalles sobre cómo la plataforma proporciona esta compatibilidad, consulta las implementaciones para los adaptadores de vinculación correspondientes:
Clase | Atributo(s) | Adaptador de vinculación |
---|---|---|
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
|
Recursos adicionales
Para obtener más información sobre la vinculación de datos, consulta los siguientes recursos adicionales.