Bidirektionale Datenbindung

Mit der unidirektionalen Datenbindung können Sie einen Wert für ein Attribut festlegen und einen Listener festlegen, der auf eine Änderung dieses Attributs reagiert:

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

Mit der bidirektionalen Datenbindung lässt sich dieser Vorgang beschleunigen:

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

Die @={}-Notation, die vor allem das Zeichen „=“ enthält, empfängt Datenänderungen an der Property und wartet gleichzeitig auf Nutzeraktualisierungen.

Wenn Sie auf Änderungen in den zugrunde liegenden Daten reagieren möchten, können Sie Ihre Layoutvariable als Implementierung von Observable festlegen (in der Regel BaseObservable) und eine @Bindable-Annotation verwenden, wie im folgenden Code-Snippet gezeigt:

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

Da die Getter-Methode des bindbaren Attributs getRememberMe() heißt, verwendet die entsprechende Setter-Methode des Attributs automatisch den Namen setRememberMe().

Weitere Informationen zur Verwendung von BaseObservable und @Bindable finden Sie unter Mit beobachtbaren Datenobjekten arbeiten.

Bidirektionale Datenbindung mithilfe benutzerdefinierter Attribute

Die Plattform bietet bidirektionale Implementierungen der Datenbindung für die gängigsten bidirektionalen Attribute und Änderungs-Listener, die Sie im Rahmen Ihrer Anwendung verwenden können. Wenn Sie die bidirektionale Datenbindung mit benutzerdefinierten Attributen nutzen möchten, müssen Sie mit den Annotationen @InverseBindingAdapter und @InverseBindingMethod arbeiten.

Wenn Sie beispielsweise die bidirektionale Datenbindung für ein "time"-Attribut in einer benutzerdefinierten Ansicht namens MyView aktivieren möchten, führen Sie die folgenden Schritte aus:

  1. Annotieren Sie die Methode, die den Anfangswert festlegt und aktualisiert, wenn sich der Wert ändert, indem Sie @BindingAdapter verwenden:

    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. Annotieren Sie die Methode, die den Wert aus der Ansicht liest, mithilfe von @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();
    }

An dieser Stelle weiß die Datenbindung, was zu tun ist, wenn sich die Daten ändern (die mit @BindingAdapter annotierte Methode wird aufgerufen) und was aufgerufen werden soll, wenn sich das Ansichtsattribut ändert (InverseBindingListener). Sie weiß jedoch nicht, wann oder wie sich das Attribut ändert.

Dazu müssen Sie für die Ansicht einen Listener festlegen. Dabei kann es sich um einen benutzerdefinierten Listener handeln, der mit Ihrer benutzerdefinierten Ansicht verknüpft ist, oder um ein allgemeines Ereignis wie Fokusverlust oder Textänderung. Fügen Sie der Methode, die den Listener für Änderungen an der Eigenschaft festlegt, die Anmerkung @BindingAdapter hinzu:

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

Der Listener enthält einen InverseBindingListener als Parameter. Mit InverseBindingListener teilen Sie dem Datenbindungssystem mit, dass sich das Attribut geändert hat. Das System kann dann die mit @InverseBindingAdapter annotierte Methode aufrufen usw.

In der Praxis enthält dieser Listener einige nicht triviale Logik, einschließlich Listener für unidirektionale Datenbindungen. Ein Beispiel finden Sie im Adapter für die Textattributänderung TextViewBindingAdapter.

Nutzer mit Conversion

Wenn die an ein View-Objekt gebundene Variable vor der Anzeige formatiert, übersetzt oder geändert werden muss, kann ein Converter-Objekt verwendet werden.

Nehmen wir z. B. ein EditText-Objekt, das ein Datum anzeigt:

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

Das Attribut viewmodel.birthDate enthält einen Wert vom Typ Long. Es muss daher mit einem Converter formatiert werden.

Da ein bidirektionaler Ausdruck verwendet wird, muss auch ein Inverse Converter vorhanden sein, damit die Bibliothek weiß, wie der vom Nutzer bereitgestellte String zurück in den unterstützenden Datentyp konvertiert wird, in diesem Fall Long. Dazu wird einem der Konverter die Annotation @InverseMethod hinzugefügt und diese Annotation auf den Inverse-Converter verweisen. Ein Beispiel für diese Konfiguration wird im folgenden Code-Snippet angezeigt:

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

Endlosschleifen mit bidirektionaler Datenbindung

Achten Sie bei der Verwendung der bidirektionalen Datenbindung darauf, keine Endlosschleifen einzuführen. Wenn der Nutzer ein Attribut ändert, wird die mit @InverseBindingAdapter annotierte Methode aufgerufen und der Wert der unterstützenden Property zugewiesen. Dies wiederum würde die mit @BindingAdapter annotierte Methode aufrufen, die einen weiteren Aufruf der mit @InverseBindingAdapter annotierten Methode auslösen usw.

Aus diesem Grund ist es wichtig, mögliche Endlosschleifen zu durchbrechen, indem neue und alte Werte in den mit @BindingAdapter annotierten Methoden verglichen werden.

Zweiwege-Attribute

Die Plattform bietet integrierte Unterstützung für die bidirektionale Datenbindung, wenn Sie die Attribute in der folgenden Tabelle verwenden. Weitere Informationen dazu, wie die Plattform diese Unterstützung bietet, finden Sie in den Implementierungen für die entsprechenden Bindungsadapter:

Klasse Attribut(e) Bindungsadapter
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

Weitere Informationen

Weitere Informationen zur Datenbindung finden Sie in den folgenden zusätzlichen Ressourcen.

Produktproben

Codelabs

Blogposts