Generated binding classes学习

Android开发文档翻译

Posted by GL on August 15, 2018

Data Binding Library生成用于访问布局文件中的variable和views的绑定类。这篇文字主要讲怎样创建和自定义生成绑定类。

生成的绑定类连接了布局文件中的layout variables和views。绑定类的名字和包名可以自定义。所有的绑定类都继承于ViewDataBinding类。

每个布局文件会生成一个绑定类。默认情况下,绑定类的名字是基于布局文件的名字的。如果布局文件是activity_main.xml,那么对应生成的类就是ActivityMainBinding。这个类持有来自layout properties到layout views的所有绑定,并且知道怎样通过表达式分配值。

创建绑定类

在inflating布局之后,应该立即创建绑定对象,以确保视图层次结构在与布局内的表达式绑定之前不会被修改。最常见的绑定对象到布局文件的方法是在绑定类上使用静态方法。你可以inflate视图结构并且通过inflate()方法绑定对象到视图,正如下面代码所示:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    MyLayoutBinding binding = MyLayoutBinding.inflate(getLayoutInflater());
}

这里有一个inflate()方法的可替代版本,就是除了LayoutInflater对象外使用ViewGroup对象,如下所示:

MyLayoutBinding binding = MyLayoutBinding.inflate(getLayoutInflater(), viewGroup, false);

如果布局文件通过一个不同的机制inflate,那么它要单独绑定,如下所示:

MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

有时,绑定类型是提前不可预知的。在这种情况下,可以使用DataBindingUtil类来创建,如下面代码所示:

View rootView = LayoutInflater.from(this).inflate(layoutId, parent, attachToParent);
ViewDataBinding binding = DataBindingUtil.bind(viewRoot);

如果你使用数据绑定在Fragment、ListView、或者RecyclerView适配器的item中,你也许更倾向于使用绑定类的inflate()方法,或者DataBindingUtil类,如下所示:

ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
// or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

Views with IDs

Data Binding Library在绑定类中为每一个在布局中有ID的视图创建一个不可变字段。例如,Data Binding Library根据下面布局文件中的TextView类型,创建了firstName和lastName字段。

<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
   android:id="@+id/firstName"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"
  android:id="@+id/lastName"/>
   </LinearLayout>
</layout>

数据绑定库从视图层级中提取视图,包括来自视图层次结构中的id。这种机制比在布局文件中每次调用findViewById()方法更快。

虽然ID在不用数据绑定是是没必要存在的,但是仍有一些情况下是必要的,那就是从代码访问视图。

Variables

数据绑定库为在布局文件中每个声明的variable生成了访问方法。例如,下面的布局为user、image、和note这三个variable,在绑定类中生成了setter和getter方法

<data>
   <import type="android.graphics.drawable.Drawable"/>
   <variable name="user" type="com.example.User"/>
   <variable name="image" type="Drawable"/>
   <variable name="note" type="String"/>
</data>

ViewStubs

不像常规的视图,ViewStub对象一开始是一个不可见的view。当它们变得可见或者显示的inflate时,它们通过inflate另一个布局来替换自己的布局。

因为ViewStub本质上是从View的层级结构中消失了,绑定对象的的视图也必须消失,以便于在垃圾回收器中声明。因为视图是final的,所以ViewStubProxy对象在生成的绑定类中代替ViewStub,当ViewStub存在时,它允许你访问ViewStub,并且当ViewStub被inflate时,它也可以访问inflate的视图层次结构。

当inflate另一个布局时,必须为新的布局建立一个绑定。因此,ViewStubProxy必须监听ViewStub的OnInflateListener,并在需要时建立绑定。因为只有一个监听器可以在给定的时间存在,所以ViewStubProxy允许你设置一个onInflateListener,它在建立绑定之后调用它。

直接绑定

当variable或observable对象发生变化时,绑定将在下一帧之前更改。然而,有时必须立即执行绑定。要强制执行,请使用executePendingBindings()方法。

提前绑定

动态的variable

有时,具体的绑定类是未知的。例如,一个RecyclerView.Adapter针对任意布局不知道具体的绑定类。它仍然必须在调用onBindViewHolder()方法时分配绑定值。

在下面的例子中,RecyclerView绑定的所有布局都有一个item variable。BindingHolder对象有一个getBinding()方法返回了ViewDataBinding基类。

public void onBindViewHolder(BindingHolder holder, int position) {
    final T item = mItems.get(position);
    holder.getBinding().setVariable(BR.item, item);
    holder.getBinding().executePendingBindings();
}

注意:数据绑定库在模块包中生成一个名为BR的类,其中包含用于数据绑定的资源的id。在上面的例子中,数据绑定库自动生成BR.item variable。

后台线程

您可以在后台线程中更改数据模型,只要它不是集合。数据绑定在评估期间将每个variable/字段本地化,以避免任何并发性问题。

自定义绑定类的名字

默认地,自己与布局文件的名字生成了一个绑定类,以大写字母开头,移除下划线,复制后面的字符,并且加上了Binding单词作为后缀。这个类被放置在模块包下的一个databinding包中。例如,布局文件名为contract_item.xml生成了ContractItemBinding类。如果module包是com.example.my.app,那么绑定类被放置在com.example.my.app.databinding包中。

绑定类也许通过调整data元素的class属性被重命名或者被放置在不同的包下。例如,下面的布局在现在module下databinding包中生成了ContractItem绑定类:

<data class="ContactItem">
    
</data>

你可以通过在一个period内预先固定类名来在另一个包中生成绑定类。下面的例子在module包中生成绑定类:

<data class=".ContactItem">
    
</data>

你也可以在你想绑定类的地方使用全包名来生成。下面的例子在com.example包中创建了ContractItem绑定类:

<data class="com.example.ContactItem">
    
</data>