Vinculación de datos en dos direcciones

Con la vinculación de datos unidireccional, puedes definir un valor en un atributo y definir una objeto de escucha que reacciona 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 los datos cambios en la propiedad y escuchar las actualizaciones de los usuarios al mismo tiempo.

Para reaccionar a los cambios en los datos de la copia de seguridad, puedes hacer que tu diseño una implementación de Observable, que suele BaseObservable y usa 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 el método set correspondiente de la propiedad usa automáticamente el nombre setRememberMe()

Para obtener más información sobre el uso de BaseObservable y @Bindable, consulta Trabaja con objetos de datos observables.

Vinculación de datos bidireccional con atributos personalizados

La plataforma proporciona implementaciones de vinculación de datos bidireccional para las bidireccionales y cambiar objetos de escucha, que puedes usar como parte de tu app. Si quieres usar la vinculación de datos bidireccional con los atributos, debes trabajar con el @InverseBindingAdapter y @InverseBindingMethod anotaciones.

Por ejemplo, si quieres habilitar la vinculación de datos bidireccional en un atributo "time" En una vista personalizada llamada MyView, completa los siguientes pasos:

  1. Anota el método que establece el valor inicial y se actualiza cuando el valor Cambios 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;
        }
    }
  2. Anota el método que lee el valor de la vista usando @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 a método anotado con @BindingAdapter) y qué cuando cambia el atributo de vista (llama a 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 asociada con tu 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 configura 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. Se usa el InverseBindingListener para indicarle al sistema de vinculación de datos que el atributo tiene cambió. Luego, el sistema puede comenzar a llamar al método anotado usando @InverseBindingAdapter, etcétera.

En la práctica, este objeto de escucha incluye una lógica no trivial, incluidos los objetos de escucha para la vinculación de datos unidireccional. Para ver un ejemplo, consulta el adaptador del atributo de texto. cambio, TextViewBindingAdapter

Converters

Si la variable que está vinculada a un objeto View debe formatearse, traducirse o cambiarse 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 formateados con un convertidor.

Debido a que se usa una expresión bidireccional, también debe haber una inversión converter para que la biblioteca sepa cómo volver a convertir la string proporcionada por el usuario al tipo de datos de copia de seguridad, en este caso, Long. Este proceso se realiza agregando la anotación @InverseMethod en uno de los convertidores y hacer que esta anotación haga referencia a la inversa convertidor. En el siguiente código, aparece un ejemplo de esta configuración snippet:

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. Cuándo el usuario cambia un atributo, el método anotado usando Se llama a @InverseBindingAdapter y se asigna el valor a la copia de seguridad propiedad. Esto, a su vez, llamaría al método anotado usando @BindingAdapter, que activaría otra llamada al método anotado usando @InverseBindingAdapter, etcétera.

Por este motivo, es importante romper posibles bucles infinitos comparando valores nuevos y antiguos en los métodos anotados con @BindingAdapter.

Atributos bidireccionales

La plataforma brinda compatibilidad integrada con la vinculación de datos bidireccional cuando se usa 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 vínculos: recursos adicionales.

Ejemplos

Codelabs

Entradas de blog