[TOC]
Jetpack学习6—双向数据绑定
使用单向数据绑定,您可以在属性上设置一个值,并设置一个侦听器来响应该属性中的更改:
<CheckBox
android:id="@+id/rememberMeCheckBox"
android:checked="@{viewmodel.rememberMe}"
android:onCheckedChanged="@{viewmodel.rememberMeChanged}"
/>
双向数据绑定为这个过程提供了一个快捷方式:
<CheckBox
android:id="@+id/rememberMeCheckBox"
android:checked="@={viewmodel.rememberMe}"
/>
包含"="的@{}
注解,为属性接收数据变化并同时监听用户更新。
为了对后台数据的变化作出反应,您可以使布局变量实现Observable
(通常是BaseObservable
),并使用@Bindable
注解,如下面的代码片段所示:
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);
}
}
}
因为可绑定属性的getter方法称为getmemorberme()
,所以属性对应的setter方法会自动使用setmemorberme()
这个名称。
有关使用BaseObservable
和的更多信息@Bindable
,请参阅使用可观察数据对象。
使用自定义属性进行双向数据绑定
该平台为最常见的双向属性和change listeners提供了双向数据绑定实现,您可以将其作为应用程序的一部分使用。如果您想使用带有自定义属性的双向数据绑定,您需要使用@InverseBindingAdapter
和@InverseBindingMethod
注解。
例如,如果您希望在名为MyView
的自定义视图中对“time”属性启用双向数据绑定,请完成以下步骤:
1.使用@BindingAdapter
注解设置初始值并在值更改时更新的方法:
@BindingAdapter("time")
public static void setTime(MyView view, Time newValue) {
// Important to break potential infinite loops.
if (view.time != newValue) {
view.time = newValue;
}
}
2.使用@InverseBindingAdapter
注解从视图中读取值的方法:
@InverseBindingAdapter("time")
public static Time getTime(MyView view) {
return view.getTime();
}
此时,数据绑定知道数据更改时要做什么(它调用带注释的方法 @BindingAdapter
)以及视图属性更改时调用什么(它调用 InverseBindingListener
)。但是,它不知道属性何时或如何更改。
为此,您需要在视图上设置一个侦听器。它可以是与自定义视图关联的自定义侦听器,也可以是通用事件,例如失去焦点或文本更改。将@BindingAdapter
注释添加到为该属性的更改设置侦听器的方法上:
@BindingAdapter("app:timeAttrChanged")
public static void setListeners(
MyView view, final InverseBindingListener attrChange) {
// Set a listener for click, focus, touch, etc.
}
监听器包括InverseBindingListener
作为参数。使用 InverseBindingListener
来告诉数据绑定系统该属性已更改。然后,系统可以开始调用使用注释的方法 @InverseBindingAdapter
,依此类推。
注意:每个双向绑定都会生成一个合成事件属性。此属性与基本属性具有相同的名称,但它包含后缀 "AttrChanged"
。合成事件属性允许库创建使用@BindingAdapter
标注的方法,以将事件侦听器关联到视图的适当实例。
转换器
如果绑定到View
对象的变量需要在显示之前以某种方式进行格式化,翻译或更改,则可以使用Converter
对象。
以显示日期的EditText对象为例:
<EditText
android:id="@+id/birth_date"
android:text="@={Converter.dateToString(viewmodel.birthDate)}"
/>
该viewmodel.birthDate
属性包含一个Long
类型的值,因此需要使用转换器对其进行格式化。
因为使用的是双向表达式,所以还需要一个反向转换器,让库知道如何将用户提供的字符串转换回支持的数据类型(在本例中为Long)。这个过程是通过向其中一个转换器添加@InverseMethod
注解来完成的,并让这个注释引用逆转换器。下面的代码片段显示了这种配置的一个示例:
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.
}
}
使用双向数据绑定的无限循环
在使用双向数据绑定时,小心不要引入无限循环。当用户更改属性时,将调用使用@InverseBindingAdapter
注释的方法,并将该值分配给支持属性。反过来,这会调用使用@BindingAdapter
注释的方法,这会触发对使用@InverseBindingAdapter
注释的方法的另一个调用,以此类推。
因此,通过比较使用@BindingAdapter
标注的方法中的新值和旧值来打破可能的无限循环非常重要。
双向属性
当您使用下表中的属性时,该平台提供了对双向数据绑定的内置支持。有关平台如何提供这种支持的详细信息,请参见相应绑定适配器的实现:
Class | Attribute(s) | Binding adapter |
---|---|---|
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 |
其他资源
GitHub上的 TwoWaySample提供了本页讨论的概念的双向示例。
网友评论