Liaison de données bidirectionnelle

La liaison de données unidirectionnelle vous permet de définir une valeur pour un attribut et de définir un écouteur qui réagit à une modification de cet attribut:

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

La liaison de données bidirectionnelle fournit un raccourci vers ce processus:

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

La notation @={}, qui inclut principalement le signe "=", reçoit les modifications de données apportées à la propriété et écoute les mises à jour des utilisateurs en même temps.

Pour réagir aux modifications des données de sauvegarde, vous pouvez faire de votre variable de mise en page une implémentation de Observable, généralement BaseObservable, et utiliser une annotation @Bindable, comme indiqué dans l'extrait de code suivant:

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);
        }
    }
}

Étant donné que la méthode getter de la propriété pouvant être liée est appelée getRememberMe(), la méthode setter correspondante de la propriété utilise automatiquement le nom setRememberMe().

Pour en savoir plus sur l'utilisation de BaseObservable et de @Bindable, consultez Utiliser des objets de données observables.

Liaison de données bidirectionnelle utilisant des attributs personnalisés

La plate-forme fournit des implémentations de liaison de données bidirectionnelle pour les attributs bidirectionnels les plus courants et les écouteurs de modifications, que vous pouvez utiliser dans votre application. Si vous souhaitez utiliser la liaison de données bidirectionnelle avec des attributs personnalisés, vous devez utiliser les annotations @InverseBindingAdapter et @InverseBindingMethod.

Par exemple, si vous souhaitez activer la liaison de données bidirectionnelle sur un attribut "time" dans une vue personnalisée appelée MyView, procédez comme suit:

  1. Annotez la méthode qui définit la valeur initiale et se met à jour lorsque la valeur change à l'aide de @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. Annotez la méthode qui lit la valeur à partir de la vue à l'aide de @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();
    }

À ce stade, la liaison de données sait quoi faire en cas de modification des données (elle appelle la méthode annotée avec @BindingAdapter) et quoi appeler lorsque l'attribut de vue change (InverseBindingListener). Toutefois, elle ne sait pas quand ni comment l'attribut change.

Pour ce faire, vous devez définir un écouteur au niveau de la vue. Il peut s'agir d'un écouteur personnalisé associé à votre vue personnalisée ou d'un événement générique, tel qu'une perte de focus ou un changement de texte. Ajoutez l'annotation @BindingAdapter à la méthode qui définit l'écouteur pour les modifications apportées à la propriété:

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.
}

L'écouteur inclut un InverseBindingListener en tant que paramètre. Vous utilisez InverseBindingListener pour indiquer au système de liaison de données que l'attribut a été modifié. Le système peut alors commencer à appeler la méthode annotée à l'aide de @InverseBindingAdapter, et ainsi de suite.

En pratique, cet écouteur inclut une logique complexe, y compris des écouteurs pour la liaison de données unidirectionnelle. Pour obtenir un exemple, consultez l'adaptateur pour la modification de l'attribut de texte, TextViewBindingAdapter.

Visiteurs ayant déjà réalisé une conversion

Si la variable liée à un objet View doit être mise en forme, traduite ou modifiée avant d'être affichée, vous pouvez utiliser un objet Converter.

Prenons l'exemple d'un objet EditText qui affiche une date:

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

L'attribut viewmodel.birthDate contient une valeur de type Long. Elle doit donc être formatée à l'aide d'un convertisseur.

Étant donné qu'une expression bidirectionnelle est utilisée, il doit également y avoir un convertisseur inverse pour indiquer à la bibliothèque comment reconvertir la chaîne fournie par l'utilisateur en type de données de sauvegarde, dans ce cas Long. Pour ce faire, vous devez ajouter l'annotation @InverseMethod à l'un des convertisseurs pour que cette annotation fasse référence au convertisseur inverse. Un exemple de cette configuration apparaît dans l'extrait de code suivant:

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.
    }
}

Boucles infinies utilisant une liaison de données bidirectionnelle

Veillez à ne pas introduire de boucles infinies lorsque vous utilisez la liaison de données bidirectionnelle. Lorsque l'utilisateur modifie un attribut, la méthode annotée à l'aide de @InverseBindingAdapter est appelée et la valeur est attribuée à la propriété de sauvegarde. Cette action appelle à son tour la méthode annotée à l'aide de @BindingAdapter, ce qui déclenche un autre appel de la méthode annotée à l'aide de @InverseBindingAdapter, et ainsi de suite.

C'est pourquoi il est important de briser les éventuelles boucles infinies en comparant les valeurs nouvelles et anciennes dans les méthodes annotées avec @BindingAdapter.

Attributs à double sens

La plate-forme est compatible avec la liaison de données bidirectionnelle lorsque vous utilisez les attributs du tableau suivant. Pour en savoir plus sur la prise en charge de cette fonctionnalité par la plate-forme, consultez les implémentations des adaptateurs de liaison correspondants:

Classe Attribut(s) Adaptateur de liaison
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

Ressources supplémentaires

Pour en savoir plus sur la liaison de données, consultez les ressources supplémentaires suivantes.

Exemples

Ateliers de programmation

Articles de blog