Ao usar a vinculação unidirecional de dados, é 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 da propriedade e ouve as atualizações dos usuários ao mesmo tempo.
Para reagir a mudanças nos dados de apoio, é possível tornar sua variável de layout uma implementação de Observable
, geralmente BaseObservable
, e usar um @Bindable
, conforme mostrado no snippet de código a seguir.
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 ver mais informações sobre como usar 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 bidirecional de dados para os atributos bidirecionais mais comuns e listeners de mudanças, que você pode usar como parte do seu app. Se você quiser usar vinculação bidirecional de dados com atributos personalizados, precisará trabalhar com as anotações @InverseBindingAdapter
e @InverseBindingMethod
.
Por exemplo, se você quiser ativar a vinculação bidirecional de dados em um atributo "time"
de uma visualização personalizada chamada MyView
, conclua as seguintes etapas:
Anote o método que define o valor inicial e atualiza quando o valor é alterado 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; } }
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(); }
Nesse momento, a vinculação de dados sabe o que fazer quando os dados são alterados (ela chama o método anotado com @BindingAdapter
) e o que chamar quando o atributo de visualização mudar (ele chama o InverseBindingListener
). No entanto, ele 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 à sua 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 as alterações do listener 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. Use o InverseBindingListener
para informar ao sistema de vinculação de dados que o atributo 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 da alteração de atributo de texto, TextViewBindingAdapter
.
Converters
Se a variável vinculada a um objeto View
precisar ser formatada, traduzida ou alterada de alguma forma antes de ser exibida, será 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
. Portanto, ele precisa ser formatado usando um conversor.
Como uma expressão bidirecional está sendo usada, também é necessário haver um conversor inverso para permitir que a biblioteca saiba como converter a string fornecida pelo usuário de volta ao tipo de dados de backup, neste caso Long
. Esse processo é feito adicionando a anotação @InverseMethod
a um dos conversores e fazendo com que ela 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
, que acionaria outra chamada para o método anotado usando @InverseBindingAdapter
e assim por diante.
Por esse motivo, é importante interromper possíveis loops infinitos comparando valores novos e antigos nos métodos anotados usando @BindingAdapter
.
Atributos bidirecionais
A plataforma oferece compatibilidade integrada para vinculação de dados bidirecional quando os atributos são usados na tabela a seguir. Para ver informações sobre como a plataforma oferece essa compatibilidade, 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 seguintes recursos adicionais.