DataBindingGuidegoogle官方文档翻译(下)

转载:http://www.it165.net/pro/html/201601/59963.html

一,数据对象

任何不含业务逻辑的java简单对象(POJO)可用于数据绑定,但修改POJO不能使UI更新。而通过数据绑定可以使数据对象感知到数据的变化。有三种不同的感知数据改变的机制,可见对象,可见字段,和可见集合。

当一个可见数据对象绑定到用户界面和数据对象变化的属性时,用户界面将自动更新。

可见对象

一个实现可见接口的类,允许把监听器和对象绑定,以便监听该对象的所有属性的变化。

可见接口有添加和删除侦听器的机制,但数据改变的通知机制取决于开发者。为了使开发更简洁,创建一个基类(BaseObservable)来实现监听器注册机制。当属性改变时,数据实现类仍负责告知机制,其中getter方法加@Bindable注释,并在setter方法中告知属性的变化。

 

view source print ?
01. private static class User extends BaseObservable {
02. private String firstName;
03. private String lastName;
04. @Bindable
05. public String getFirstName() {
06. return this.firstName;
07. }
08. @Bindable
09. public String getLastName() {
10. return this.lastName;
11. }
12. public void setFirstName(String firstName) {
13. this.firstName = firstName;
14. notifyPropertyChanged(BR.firstName);
15. }
16. public void setLastName(String lastName) {
17. this.lastName = lastName;
18. notifyPropertyChanged(BR.lastName);
19. }
20. }
在编译过程中,绑定注释在BR类文件中生成一个条目,然后在模块包中生成BR类文件。如果数据类的基类不可改变,可以使用方便PropertyChangeRegistry有效地存储和通知侦听器的方式来实现可见接口。

 

可见字段

创建可见类的过程中,开发人员想节省时间或有很多属性时可以使用可见字段,例如一些常用的可见类型字段:ObservableBoolean, ObservableByte, ObservableChar,ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, andObservableParcelable。

可见字段是有独立字段的可见对象。原始版本在访问操作中避免装箱和拆箱操作。为方便使用,在数据类创建使用public final修饰的字段。

 

view source print ?
1. private static class User {
2. public final ObservableField firstName =
3. new ObservableField<>();
4. public final ObservableField lastName =
5. new ObservableField<>();
6. public final ObservableInt age = new ObservableInt();
7. }
通过setter和getter方法获取值。

 

 

view source print ?
1. user.firstName.set("Google");
2. int age = user.age.get();

可见集合

 

一些应用程序使用更多的动态结构来保存数据。可见集合支持键控存取方式访问这些数据对象。当键是一个像字符串这样的引用类型时,可使用ObservableArrayMap。

 

view source print ?
1. ObservableArrayMap user = new ObservableArrayMap<>();
2. user.put("firstName""Google");
3. user.put("lastName""Inc.");
4. user.put("age"17);
在布局中,可以通过引用String类型的键进行映射:

 

 

view source print ?
01.
02. <import type="android.databinding.ObservableMap"/>
03. "user" type="ObservableMap"/>
04.
05.
06.
07. android:text='@{user["lastName"]}'
08. android:layout_width="wrap_content"
09. android:layout_height="wrap_content"/>
10.
11. android:text='@{String.valueOf(1 + (Integer)user["age"])}'
12. android:layout_width="wrap_content"
13. android:layout_height="wrap_content"/>
当键是整型时,可使用ObservableArrayList。

 

 

view source print ?
1. ObservableArrayList user = new ObservableArrayList<>();
2. user.add("Google");
3. user.add("Inc.");
4. user.add(17);
在布局中,可通过索引访问列表。

 

 

view source print ?
01.
02. <import type="android.databinding.ObservableList"/>
03. <import type="com.example.my.app.Fields"/>
04. "user" type="ObservableList"/>
05.
06.
07.
08. android:text='@{user[Fields.LAST_NAME]}'
09. android:layout_width="wrap_content"
10. android:layout_height="wrap_content"/>
11.
12. android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
13. android:layout_width="wrap_content"
14. android:layout_height="wrap_content"/>

 

生成绑定

 

任何不含业务逻辑的java简单对象(POJO)可用于数据绑定,但修改POJO不能使UI更新。而通过数据绑定可以使数据对象感知到数据的变化。有三种不同的感知数据改变的机制,可见对象,可见字段,和可见集合。

当一个可见数据对象绑定到用户界面和数据对象变化的属性时,用户界面将自动更新。

生成绑定类链接在布局视图的布局变量,正如前面所讨论的,绑定的名称和包可以自定义。生成的绑定类都继承ViewDataBinding。

应该立即创建绑定,以确保布局中的表达式与视图的绑定不干扰视图层。有几种绑定到布局的方法。最常见的是在绑定类调用静态方法inflate。inflate解析视图,并完成数据绑定。还有一个更简单的版本,只需要在inflate方法中引入LayoutInflater或再加上ViewGroup:

 

view source print ?
1. MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
2. MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);

 

如果布局inflate机制有变化,也可以使用分开绑定的机制。

 

view source print ?
1. MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
在无法预先知道是否绑定的情况下,可以使用DataBindingUtil 类创建绑定:

 

 

view source print ?
1. ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
2. parent, attachToParent);
3. ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);

 

有ID的视图

 

在布局中每个有ID的视图将相应生成一个public final字段的变量,绑定类只调用一次布局,并创建每个视图。这种机制比调用findViewById方法更有效率。

 

view source print ?
01. "http://schemas.android.com/apk/res/android">
02.
03. "user" type="com.example.User"/>
04.
05.
06. android:orientation="vertical"
07. android:layout_width="match_parent"
08. android:layout_height="match_parent">
09. "wrap_content"
10. android:layout_height="wrap_content"
11. android:text="@{user.firstName}"
12. android:id="@+id/firstName"/>
13. "wrap_content"
14. android:layout_height="wrap_content"
15. android:text="@{user.lastName}"
16. android:id="@+id/lastName"/>
17.
18.
会生成带有如下属性的绑定类:

 

 

view source print ?
1. public final TextView firstName;
2. public final TextView lastName;
ID可能没有数据绑定那样必要,但是仍有一些实例证明,从代码访问角度看仍然是必要的。

 

二、变量

每个变量会给出存取方法:

 

view source print ?
1.
2. <import type="android.graphics.drawable.Drawable"/>
3. "user"  type="com.example.User"/>
4. "image" type="Drawable"/>
5. "note"  type="String"/>
6.
在绑定中生成setter和getter方法:

 

 

view source print ?
1. public abstract com.example.User getUser();
2. public abstract void setUser(com.example.User user);
3. public abstract Drawable getImage();
4. public abstract void setImage(Drawable image);
5. public abstract String getNote();
6. public abstract void setNote(String note);

 

ViewStubs

 

viewstubs与普通视图不同。他们开始是是一个不可视并且大小为0的视图,可以延迟到运行时填充布局资源。设置为Visible或调用inflate()之后,就会填充布局资源,ViewStub便会被填充的视图替代。

由于ViewStub会在视图层消失,为了正常的连接,对应的绑定对象也要随之消失。由于视图层是final类型的,ViewStubProxy对象替代ViewStub 后,开发者可以访问 ViewStub,并且当 ViewStub 在视图层中被加载时,开发者也可以访问加载的视图。

当解析其他布局时,新的布局中要建立绑定关系。因此,ViewStubProxy 对象要监听ViewStub的OnInflateListener并建立绑定。开发者可以在ViewStubProxy 对象上建立一个OnInflateListener,绑定建立后,便可调用OnInflateListener。

高级绑定

动态变量

有时具体的绑定类会不被识别。例如,操作任意布局的RecyclerView.Adapter必须在onBindViewHolder中指定绑定值,才可识别出对应的绑定类。

在这个例子中,RecyclerView绑定的所有布局都有一个item变量。BindingHolder 对象通过引用getBinding() 方法获取 ViewDataBinding 基类。

 

view source print ?
1. public void onBindViewHolder(BindingHolder holder, int position) {
2. final T item = mItems.get(position);
3. holder.getBinding().setVariable(BR.item, item);
4. holder.getBinding().executePendingBindings();
5. }

 

立即绑定

 

当变量变化时,绑定在下一帧到来前也要随之改变。然而,当绑定需要立即执行时,可以调用强制执行方法executePendingBindings()。

后台线程

可以在后台线程中改变数据模型,只要它不是集合。数据绑定将每个变量或字段保存到本地,以避免任何并发问题。

属性Setters

只要绑定值有变化,生成的绑定类就会在视图中调用setter方法。数据绑定框架可以自定义要调用的方法来设置值。

自动Setters

对一个属性来说,数据绑定试图查找setAttribute方法。这与属性的命名空间没有关系,只与属性本身有关。

例如 TextView 的属性 android:text 上的表达式,数据绑定将查找setText(String) 方法,如果表达式返回值为 int,则会调用 setText(int)方法。注意表达式要返回正确的类型,有必要则使用cast进行类型转换。即使没有给定的属性,数据绑定也会执行。可以通过数据绑定调用setter方法创建属性。例如,DrawerLayout没有任何属性,但有很多setter方法,可以调用setter方法自动创建属性。

 

view source print ?
1.
2. android:layout_width="wrap_content"
3. android:layout_height="wrap_content"
4. app:scrimColor="@{@color/scrim}"
5. app:drawerListener="@{fragment.drawerListener}"/>

 

重命名Setters

 

对于属性setter方法与名字不匹配的情况,可以通过BindingMethods注释关联名字和方法。类中包含BindingMethod注释,其中可以重命名set方法。例如,android:tint 属性与setImageTintList(ColorStateList)方法相关,而与setTint不对应。

 

view source print ?
1. @BindingMethods({
2. @BindingMethod(type = "android.widget.ImageView",
3. attribute = "android:tint",
4. method = "setImageTintList"),
5. })
开发者不需要重命名setter方法,安卓框架已经做好了这方面的工作。

 

自定义 Setters

一些属性需要自定义绑定逻辑,android:paddingLeft 属性并没有对应的setter方法,但是存在setPadding(left, top, right, bottom)方法。通过 BindingAdapter 注释来自定义属性调用的静态setter方法。android 系统已经创建了 BindingAdapter 函数,下面是 paddingLeft 属性对应的函数:

 

view source print ?
1. @BindingAdapter("android:paddingLeft")
2. public static void setPaddingLeft(View view, int padding) {
3. view.setPadding(padding,
4. view.getPaddingTop(),
5. view.getPaddingRight(),
6. view.getPaddingBottom());
7. }
绑定适配器对于其他自定义类型很有帮助。例如可以在其他线程中自定义加载图片。如果绑定适配器有冲突,则开发者自定义的将会重写系统默认的绑定适配方法。一个适配器还可以有多个参数:

 

 

view source print ?
1. @BindingAdapter({"bind:imageUrl""bind:error"})
2. public static void loadImage(ImageView view, String url, Drawable error) {
3. Picasso.with(view.getContext()).load(url).error(error).into(view);
4. }
5.
6. app:error=“@{@drawable/venueError}”/>
如果用于 ImageView 的 imageUrl和 error 参数都存在并且 imageUrl 是 string 类型、error 是 drawable 类型 则就会调用上面定义的适配器。

 

在匹配适配器的时候, 会忽略自定义的命名空间你也可以为 android 命名空间的属性自定义适配器。

 

view source print ?
01. @BindingAdapter("android:paddingLeft")
02. public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
03. if (oldPadding != newPadding) {
04. view.setPadding(newPadding,
05. view.getPaddingTop(),
06. view.getPaddingRight(),
07. view.getPaddingBottom());
08. }
09. }
只能使用一个抽象方法的接口或抽象类调用事件处理程序。

 

 

view source print ?
01. @BindingAdapter("android:onLayoutChange")
02. public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
03. View.OnLayoutChangeListener newValue) {
04. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
05. if (oldValue != null) {
06. view.removeOnLayoutChangeListener(oldValue);
07. }
08. if (newValue != null) {
09. view.addOnLayoutChangeListener(newValue);
10. }
11. }
12. }
当一个监听器有多种方法时,必须要分割成多个监听器。

 

例如,OnAttachStateChangeListener包含两个方法, onViewAttachedToWindow() 和onViewDetachedFromWindow(),必须创建两个接口用于区分属性和处理程序。

 

view source print ?
01. @TargetApi(VERSION_CODES.HONEYCOMB_MR1)
02. public interface OnViewDetachedFromWindow {
03. void onViewDetachedFromWindow(View v);
04. }
05.  
06. @TargetApi(VERSION_CODES.HONEYCOMB_MR1)
07. public interface OnViewAttachedToWindow {
08. void onViewAttachedToWindow(View v);
09. }
一个监听器的改变也会影响另外一个,所以必须有三个不同的绑定适配器,每个监听器分别对应一个,两个监听器对应第三个绑定适配器。

 

 

view source print ?
01. @BindingAdapter("android:onViewAttachedToWindow")
02. public static void setListener(View view, OnViewAttachedToWindow attached) {
03. setListener(view, null, attached);
04. }
05. @BindingAdapter("android:onViewDetachedFromWindow")
06. public static void setListener(View view, OnViewDetachedFromWindow detached) {
07. setListener(view, detached, null);
08. }
09. @BindingAdapter({"android:onViewDetachedFromWindow""android:onViewAttachedToWindow"})
10. public static void setListener(View view, final OnViewDetachedFromWindow detach,
11. final OnViewAttachedToWindow attach) {
12. if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
13. final OnAttachStateChangeListener newListener;
14. if (detach == null && attach == null) {
15. newListener = null;
16. else {
17. newListener = new OnAttachStateChangeListener() {
18. @Override
19. public void onViewAttachedToWindow(View v) {
20. if (attach != null) {
21. attach.onViewAttachedToWindow(v);
22. }
23. }
24. @Override
25. public void onViewDetachedFromWindow(View v) {
26. if (detach != null) {
27. detach.onViewDetachedFromWindow(v);
28. }
29. }
30. };
31. }
32. final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
33. newListener, R.id.onAttachStateChangeListener);
34. if (oldListener != null) {
35. view.removeOnAttachStateChangeListener(oldListener);
36. }
37. if (newListener != null) {
38. view.addOnAttachStateChangeListener(newListener);
39. }
40. }
41. }
上面的例子比正常的要稍微复杂,因为视图对监听器用添加和删除方法替代set方法。

 

android.databinding.adapters.ListenerUtil类帮助跟踪之前的监听器,以便在绑定适配器中及时移除相应的监听器。

通过用@TargetApi(VERSION_CODES.HONEYCOMB_MR1)注释接口OnViewDetachedFromWindow and OnViewAttachedToWindow 。数据绑定代码生成器知道只能在产生蜂窝MR1和新设备中运行时才会生成监听器。

转换器(Converters)

对象转换

当绑定表达式返回一个对象时候,将会自动调用 set 函数、重命名的函数、或者自定义的 setter 中的一个。表达式返回的对象将会转换为该函数的参数类型。

使用 ObservableMaps 来保存数据会比较简单。例如:

 

view source print ?
1.
2. android:text='@{userMap["lastName"]}'
3. android:layout_width="wrap_content"
4. android:layout_height="wrap_content"/>
这里的 userMap 返回的对象将制动转换为 setText(CharSequence) 的参数。 如果参数不明确,则开发者需要强制转换为需要的类型。

 

自定义转换规则

有时候参数应该可以自动转换,例如

 

view source print ?
1.
2. android:background="@{isError ? @color/red : @color/white}"
3. android:layout_width="wrap_content"
4. android:layout_height="wrap_content"/>
上面的背景需要一个 Drawable 对象,但是表达式的返回值为整数对象的颜色值。这种情况下,颜色值需要转换为 ColorDrawable。 这种转换通过一个静态函数完成,该函数带有一个 BindingConversion 注解。

 

 

view source print ?
1. @BindingConversion
2. public static ColorDrawable convertColorToDrawable(int color) {
3. return new ColorDrawable(color);
4. }
需要注意的是,转换是在 setter上完成的,所以不能把不同的类型混合使用:

 

 

view source print ?
1.
2. android:background="@{isError ? @drawable/error : @color/white}"
3. android:layout_width="wrap_content"
4. android:layout_height="wrap_content"/>

 

三、Android Studio 支持数据绑定

 

Android Studio支持数据绑定表达式的语法高亮显示,并在编辑器中显示任何表达式语言语法错误。

预览窗格显示数据绑定表达式的默认值。以下从布局XML文件的元素实例摘录。

 

view source print ?
1. "wrap_content"
2. android:layout_height="wrap_content"
3. android:text="@{user.firstName, default=PLACEHOLDER}"/>

你可能感兴趣的:(Data,Binding)