Dwukierunkowe wiązanie danych

Korzystając z jednokierunkowego wiązania danych, możesz ustawić wartość atrybutu i ustawić detektor, który reaguje na zmianę tego atrybutu:

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

Dwukierunkowe wiązanie danych skraca ten proces:

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

Notacja @={}, która zawiera znak „=”, otrzymuje zmiany danych w usłudze i jednocześnie nasłuchuje aktualizacji użytkowników.

Aby reagować na zmiany w danych podstawowych, możesz ustawić zmienną układu jako implementację Observable, zwykle BaseObservable, i użyć adnotacji @Bindable, jak pokazano w tym fragmencie kodu:

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

Metoda getter właściwości możliwej do powiązania nazywa się getRememberMe(), więc odpowiadająca jej metoda ustawiająca automatycznie używa nazwy setRememberMe().

Więcej informacji o korzystaniu z BaseObservable i @Bindable znajdziesz w artykule Praca z obiektami danych dostępnych do obserwacji.

Dwukierunkowe wiązanie danych za pomocą atrybutów niestandardowych

Platforma udostępnia dwukierunkowe wiązania danych dla najpopularniejszych atrybutów dwukierunkowych i detektorów zmian, których możesz używać w swojej aplikacji. Jeśli chcesz użyć dwukierunkowego wiązania danych z atrybutami niestandardowymi, musisz pracować z adnotacjami @InverseBindingAdapter i @InverseBindingMethod.

Jeśli np. chcesz włączyć dwukierunkowe wiązanie danych dla atrybutu "time" w widoku niestandardowym o nazwie MyView, wykonaj te czynności:

  1. Za pomocą funkcji @BindingAdapter dodaj adnotacje do metody, która ustawia wartość początkową i jest aktualizowana po zmianie wartości:

    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. Dodaj adnotację do metody, która odczytuje wartość z widoku, za pomocą parametru @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();
    }

W tym momencie wiązanie danych wie, co zrobić w przypadku zmiany danych (wywołuje metodę z adnotacją @BindingAdapter), a co wywołuje po zmianie atrybutu widoku (wywołuje metodę InverseBindingListener). Nie wie jednak, kiedy i jak zmienia się atrybut.

Żeby to zrobić, musisz ustawić odbiornik w widoku danych. Może to być niestandardowy detektor powiązany z widokiem niestandardowym lub zdarzenie ogólne, takie jak utrata zaznaczenia lub zmiana tekstu. Dodaj adnotację @BindingAdapter do metody, która ustawia detektor zmian w usłudze:

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

Detektor zawiera parametr InverseBindingListener. InverseBindingListener służy do informowania systemu wiązania danych o zmianie atrybutu. System może wtedy wywoływać metodę oznaczoną adnotacjami z użyciem @InverseBindingAdapter itd.

W praktyce ten detektor obejmuje niektóre nieproste logiki, m.in. detektory jednokierunkowe wiązania danych. Przykład znajdziesz w adapterze zmiany atrybutu tekstowego TextViewBindingAdapter.

Użytkownicy dokonujący konwersji

Jeśli zmienna powiązana z obiektem View przed wyświetleniem wymaga sformatowania, przetłumaczenia lub zmiany, możesz użyć obiektu Converter.

Spójrzmy na przykład na obiekt EditText, który pokazuje datę:

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

Atrybut viewmodel.birthDate zawiera wartość typu Long, więc należy go sformatować za pomocą konwertera.

Ponieważ używane jest wyrażenie dwukierunkowe, wymagany jest też odwrotny konwerter, który informuje bibliotekę, jak przekonwertować ciąg podany przez użytkownika na typ danych podstawowych (w tym przypadku Long). Proces ten polega na dodaniu adnotacji @InverseMethod do jednego z konwertera, w której adnotacja odwołuje się do odwrotnego konwertera. Przykład tej konfiguracji znajdziesz we fragmencie kodu:

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

Pętle nieskończone z użyciem dwukierunkowego wiązania danych

Uważaj, aby nie wprowadzić zapętleń nieskończonych, jeśli używasz dwukierunkowego wiązania danych. Gdy użytkownik zmieni atrybut, wywoływana jest metoda oznaczona za pomocą @InverseBindingAdapter, a wartość jest przypisywana do właściwości zapasowej. To z kolei spowoduje wywołanie metody z adnotacją @BindingAdapter, która wywoła kolejne wywołanie metody z adnotacją @InverseBindingAdapter itd.

Z tego względu ważne jest przerwanie możliwej nieskończonej pętli przez porównanie nowych i stałych wartości w metodach z adnotacjami @BindingAdapter.

Atrybuty dwukierunkowe

Platforma zapewnia wbudowaną obsługę dwukierunkowego wiązania danych w przypadku używania atrybutów podanych w tabeli poniżej. Szczegółowe informacje o tym, jak platforma zapewnia tę obsługę, znajdziesz w implementacjach odpowiednich adapterów wiązań:

Kategoria Atrybuty Adapter do wiązania
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

Dodatkowe materiały

Więcej informacji o wiązaniu danych znajdziesz w tych dodatkowych materiałach.

Próbki

Ćwiczenia z programowania

Posty na blogu