Vinculação bidirecional de dados

Com a vinculação de dados unidirecional, é possível definir um valor em um atributo e definir um listener que reaja a uma mudança nesse atributo:

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

A vinculação bidirecional de dados fornece um atalho para esse processo:

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

A notação @={}, que inclui o sinal "=", recebe mudanças de dados na propriedade e detecta as atualizações dos usuários ao mesmo tempo.

Para reagir às mudanças nos dados de apoio, você pode transformar a variável de layout em uma implementação de Observable, geralmente BaseObservable, e usar uma anotação @Bindable, conforme mostrado no snippet de código abaixo:

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

Como o método getter da propriedade vinculável é chamado de getRememberMe(), o método setter correspondente da propriedade usa automaticamente o nome setRememberMe().

Para mais informações sobre o uso de BaseObservable e @Bindable, consulte Trabalhar com objetos de dados observáveis.

Vinculação bidirecional de dados usando atributos personalizados

A plataforma fornece implementações de vinculação de dados bidirecional para os atributos bidirecionais e listeners de mudança mais comuns, que podem ser usados como parte do seu app. Se você quiser usar a vinculação de dados bidirecional com atributos personalizados, trabalhe com as anotações @InverseBindingAdapter e @InverseBindingMethod.

Por exemplo, se você quiser ativar a vinculação de dados bidirecional em um atributo "time" em uma visualização personalizada chamada MyView, siga estas etapas:

  1. Anote o método que define o valor inicial e é atualizado quando o valor muda usando @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. Anote o método que lê o valor da visualização 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();
    }

Neste ponto, a vinculação de dados sabe o que fazer quando os dados mudam (chama o método anotado com @BindingAdapter) e o que chamar quando o atributo de visualização muda (chama InverseBindingListener). No entanto, ela não sabe quando ou como o atributo muda.

Para isso, é necessário definir um listener na visualização. Pode ser um listener personalizado associado à visualização personalizada ou um evento genérico, como perda de foco ou mudança de texto. Adicione a anotação @BindingAdapter ao método que define o listener de mudanças na propriedade:

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

O listener inclui um InverseBindingListener como parâmetro. O InverseBindingListener é usado para informar ao sistema de vinculação de dados que o atributo mudou. Então, o sistema pode começar a chamar o método anotado usando @InverseBindingAdapter e assim por diante.

Na prática, esse listener inclui uma lógica não trivial, como listeners para vinculação unidirecional de dados. Para conferir um exemplo, consulte o adaptador da mudança de atributo de texto, TextViewBindingAdapter.

Conversões

Se a variável vinculada a um objeto View precisar ser formatada, traduzida ou alterada de alguma forma antes de ser exibida, é possível usar um objeto Converter.

Por exemplo, use um objeto EditText que mostre uma data:

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

O atributo viewmodel.birthDate contém um valor do tipo Long, então ele precisa ser formatado usando um conversor.

Como uma expressão bidirecional está sendo usada, também é necessário que haja um conversor inverso para permitir que a biblioteca saiba como converter a string fornecida pelo usuário de volta para o tipo de dados de apoio, nesse caso, Long. Esse processo é feito adicionando a anotação @InverseMethod a um dos conversores e fazendo com que essa anotação faça referência ao conversor inverso. Um exemplo dessa configuração é mostrado no snippet de código a seguir:

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

Loops infinitos usando vinculação de dados bidirecional

Tenha cuidado para não iniciar loops infinitos ao usar a vinculação bidirecional de dados. Quando o usuário muda um atributo, o método anotado usando @InverseBindingAdapter é chamado, e o valor é atribuído à propriedade de apoio. Isso, por sua vez, chamaria o método anotado usando @BindingAdapter, o que acionaria outra chamada para o método anotado usando @InverseBindingAdapter e assim por diante.

Por esse motivo, é importante quebrar possíveis loops infinitos comparando valores novos e antigos dos métodos anotados usando @BindingAdapter.

Atributos bidirecionais

A plataforma oferece suporte integrado para vinculação de dados bidirecional quando você usa os atributos da tabela a seguir. Para ver detalhes sobre como a plataforma oferece esse suporte, consulte as implementações dos adaptadores de vinculação correspondentes:

Classe. Atributos Adaptador de vinculação
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

Outros recursos

Para saber mais sobre vinculação de dados, consulte os recursos adicionais a seguir.

Exemplos

Codelabs

Postagens do blog