Vinculação bidirecional de dados

Ao usar a vinculação de dados unidirecional, você pode definir um valor em um atributo e definir um que reage 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 caractere "=" assina, recebe dados alterações na propriedade e monitoram as atualizações dos usuários ao mesmo tempo.

Para reagir a alterações nos dados de apoio, você pode fazer com que seu layout uma implementação de Observable, normalmente BaseObservable, e use uma Anotação @Bindable, como mostrado em o seguinte snippet de código:

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 como usar BaseObservable e @Bindable, consulte Trabalhar com objetos de dados observáveis.

Vinculação de dados bidirecional usando atributos personalizados

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

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. Anotar o método que define o valor inicial e é atualizado quando o valor mudanças 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 método com a anotação @BindingAdapter) e o que quando o atributo de visualização muda (ele chama o método 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 pode ser um evento genérico, como uma perda de foco ou uma mudança de texto. Adicionar a anotação @BindingAdapter ao método que define o listener de alterações 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. Você usa o InverseBindingListener para informar ao sistema de vinculação de dados que o atributo tem mudou. O sistema pode então 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, incluindo listeners para vinculação unidirecional de dados. Para ver um exemplo, consulte o adaptador do atributo de texto mudar, TextViewBindingAdapter

Converters

Se a variável vinculada a um objeto View precisa 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 sejam formatados usando um conversor.

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

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 altera um atributo, o método anotado usando @InverseBindingAdapter é chamado, e o valor é atribuído à função de backup . Isso, por sua vez, chamaria o método anotado usando @BindingAdapter, que acionaria outra chamada para o método anotado usando @InverseBindingAdapter e assim por diante.

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

Atributos bidirecionais

A plataforma oferece suporte integrado para vinculação bidirecional de dados quando você usa os atributos na tabela a seguir. Para mais 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 a vinculação de dados, consulte as seguintes recursos extras.

Amostras

Codelabs

Postagens do blog