Associazione di dati bidirezionale

Utilizzando l'associazione di dati unidirezionale, puoi impostare un valore per un attributo e impostare un ascoltatore che reagisce a una modifica nell'attributo:

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

L'associazione di dati bidirezionale fornisce una scorciatoia a questa procedura:

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

La notazione @={}, che include principalmente il segno "=", riceve le modifiche ai dati della proprietà e ascolta contemporaneamente gli aggiornamenti degli utenti.

Per reagire alle modifiche nei dati di supporto, puoi rendere la tua variabile di layout un'implementazione di Observable, in genere BaseObservable, e utilizzare un'annotazione @Bindable, come mostrato nel seguente snippet di codice:

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

Poiché il metodo getter della proprietà associabile è denominato getRememberMe(), il metodo setter corrispondente della proprietà utilizza automaticamente il nome setRememberMe().

Per ulteriori informazioni sull'utilizzo di BaseObservable e @Bindable, consulta Utilizzare gli oggetti di dati osservabili.

Associazione di dati bidirezionale con attributi personalizzati

La piattaforma fornisce implementazioni di associazioni di dati bidirezionali per gli attributi bidirezionali più comuni e i listener di modifiche, che puoi utilizzare come parte della tua app. Se vuoi utilizzare l'associazione di dati bidirezionale con attributi personalizzati, devi utilizzare le annotazioni @InverseBindingAdapter e @InverseBindingMethod.

Ad esempio, se vuoi attivare l'associazione di dati bidirezionale su un attributo "time" in una vista personalizzata denominata MyView, completa i seguenti passaggi:

  1. Annota il metodo che imposta il valore iniziale e si aggiorna quando il valore cambia utilizzando @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. Annota il metodo che legge il valore dalla vista utilizzando @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();
    }

A questo punto, l'associazione di dati sa cosa fare quando i dati cambiano (chiama il metodo annotato con @BindingAdapter) e cosa chiamare quando cambia l'attributo vista (chiama InverseBindingListener). Tuttavia, non sa quando o come cambia l'attributo.

Per farlo, devi impostare un listener sulla vista. Può essere un listener personalizzato associato alla visualizzazione personalizzata o un evento generico, come una perdita di stato attivo o una modifica del testo. Aggiungi l'annotazione @BindingAdapter al metodo che imposta il listener per le modifiche nella proprietà:

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

Il listener include un InverseBindingListener come parametro. Puoi utilizzare l'InverseBindingListener per comunicare al sistema di associazione dei dati che l'attributo è stato modificato. Il sistema può quindi iniziare a chiamare il metodo annotato utilizzando @InverseBindingAdapter e così via.

In pratica, questo listener include alcune logiche non banali, inclusi i listener per l'associazione di dati unidirezionale. Ad esempio, vedi l'adattatore per la modifica dell'attributo di testo, TextViewBindingAdapter.

Utenti che hanno completato una conversione

Se la variabile associata a un oggetto View deve essere formattata, tradotta o modificata in qualche modo prima di essere visualizzata, è possibile utilizzare un oggetto Converter.

Ad esempio, supponiamo che un oggetto EditText mostri una data:

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

L'attributo viewmodel.birthDate contiene un valore di tipo Long, quindi deve essere formattato utilizzando un convertitore.

Poiché viene utilizzata un'espressione bidirezionale, deve anche essere un convertitore inverso per consentire alla libreria di sapere come convertire la stringa fornita dall'utente nel tipo di dati di supporto, in questo caso Long. Questo processo viene eseguito aggiungendo l'annotazione @InverseMethod a uno degli utenti che hanno completato una conversione e fai in modo che questa annotazione faccia riferimento al convertitore inverso. Un esempio di questa configurazione viene visualizzato nel seguente snippet di codice:

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

Loop infiniti con associazione di dati bidirezionale

Fai attenzione a non introdurre loop infiniti quando utilizzi l'associazione di dati bidirezionale. Quando l'utente modifica un attributo, viene richiamato il metodo annotato utilizzando @InverseBindingAdapter e il valore viene assegnato alla proprietà di supporto. Questo, a sua volta, chiamerà il metodo annotato utilizzando @BindingAdapter, il che attiverebbe un'altra chiamata al metodo annotato utilizzando @InverseBindingAdapter e così via.

Per questo motivo è importante interrompere possibili loop infiniti confrontando valori nuovi e vecchi nei metodi annotati utilizzando @BindingAdapter.

Attributi bidirezionali

La piattaforma fornisce supporto integrato per l'associazione di dati bidirezionale quando utilizzi gli attributi nella seguente tabella. Per informazioni dettagliate sul modo in cui la piattaforma fornisce questo supporto, consulta le implementazioni degli adattatori di associazione corrispondenti:

Classe Attributi Adattatore di associazione
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

Risorse aggiuntive

Per scoprire di più sull'associazione di dati, consulta le seguenti risorse aggiuntive.

Samples

Codelab

Post del blog