Android Jetpack中DataBinding数据绑定生成的绑定类(五)
生成的绑定类
数据绑定库可以生成用于访问布局的变量和视图的绑定类。本页介绍了如何创建和自定义生成的绑定类。
生成的绑定类将布局变量与布局中的视图关联起来。绑定类的名称和包可以自定义。所有生成的绑定类都是从 ViewDataBinding 类继承而来的。
系统会为每个布局文件生成一个绑定类。默认情况下,类名称基于布局文件的名称,它会转换为 Pascal 大小写形式并在末尾添加 Binding 后缀。以上布局文件名为 activity_main.xml,因此生成的对应类为 ActivityMainBinding。此类包含从布局属性(例如,user 变量)到布局视图的所有绑定,并且知道如何为绑定表达式指定值。
创建绑定对象
在对布局进行扩充后立即创建绑定对象,以确保视图层次结构在通过表达式与布局内的视图绑定之前不会被修改。将对象绑定到布局的最常用方法是在绑定类上使用静态方法。您可以使用绑定类的 inflate() 方法来扩充视图层次结构并将对象绑定到该层次结构,如以下示例所示:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: MyLayoutBinding = MyLayoutBinding.inflate(layoutInflater)
setContentView(binding.root)
}
Java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyLayoutBinding binding = MyLayoutBinding.inflate(getLayoutInflater());
setContentView(binding.root);
}
inflate() 方法还有另外一个版本,这个版本不仅使用 LayoutInflater 对象,还使用 ViewGroup 对象,如以下示例所示:
Kotlin
val binding: MyLayoutBinding = MyLayoutBinding.inflate(getLayoutInflater(), viewGroup, false)
Java
MyLayoutBinding binding = MyLayoutBinding.inflate(getLayoutInflater(), viewGroup, false);
如果布局是使用其他机制扩充的,可单独绑定,如下所示:
Kotlin
val binding: MyLayoutBinding = MyLayoutBinding.bind(viewRoot)
Java
MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
有时,系统无法预先知道绑定类型。在这种情况下,可以使用 DataBindingUtil 类创建绑定,如以下代码段所示:
Kotlin
val viewRoot = LayoutInflater.from(this).inflate(layoutId, parent, attachToParent)
val binding: ViewDataBinding? = DataBindingUtil.bind(viewRoot)
Java
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
// or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
带 ID 的视图
数据绑定库会针对布局中具有 ID 的每个视图在绑定类中创建不可变字段。例如,数据绑定库会根据以下布局创建 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 并不是必不可少的,但仍有一些情况必须能够从代码访问视图。
变量
数据绑定库为布局中声明的每个变量生成访问器方法。例如,以下布局在绑定类中针对 user、image 和 note 变量生成了 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 对象初始是一个不可见视图。当它们显示出来或者获得明确指示进行扩充时,它们会通过扩充另一个布局在布局中完成自我取代。
由于 ViewStub 实际上会从视图层次结构中消失,因此绑定对象中的视图也必须消失,才能通过垃圾回收进行回收。由于视图是最终结果,因此 ViewStubProxy 对象将取代生成的绑定类中的 ViewStub,让您能够访问 ViewStub(如果存在),同时还能访问 ViewStub 进行扩充后的扩充版视图层次结构。
在扩充其他布局时,必须为新布局建立绑定。因此,ViewStubProxy 必须监听 ViewStub OnInflateListener 并在必要时建立绑定。由于在给定时间只能有一个监听器,因此 ViewStubProxy 允许您设置 OnInflateListener,它将在建立绑定后调用这个监听器。
即时绑定
当可变或可观察对象发生更改时,绑定会按照计划在下一帧之前发生更改。但有时必须立即执行绑定。要强制执行,请使用 executePendingBindings() 方法。
高级绑定
动态变量
有时,系统并不知道特定的绑定类。例如,针对任意布局运行的 RecyclerView.Adapter 不知道特定绑定类。在调用 onBindViewHolder() 方法时,仍必须指定绑定值。
在以下示例中,RecyclerView 绑定到的所有布局都有 item 变量。BindingHolder 对象具有一个 getBinding() 方法,这个方法返回 ViewDataBinding 基类。
Kotlin
override fun onBindViewHolder(holder: BindingHolder, position: Int) {
item: T = items.get(position)
holder.binding.setVariable(BR.item, item);
holder.binding.executePendingBindings();
}
Java
public void onBindViewHolder(BindingHolder holder, int position) {
final T item = items.get(position);
holder.getBinding().setVariable(BR.item, item);
holder.getBinding().executePendingBindings();
}
注意:数据绑定库在模块包中生成一个名为 BR 的类,其中包含用于数据绑定的资源的 ID。在上例中,该库自动生成 BR.item 变量。
后台线程
您可以在后台线程中更改数据模型,但前提是这个模型不是集合。数据绑定会在求值过程中对每个变量/字段进行本地化,以避免出现并发问题。
自定义绑定类名称
默认情况下,绑定类是根据布局文件的名称生成的,以大写字母开头,移除下划线 ( _ ),将后一个字母大写,最后添加后缀 Binding。该类位于模块包下的 databinding 包中。例如,布局文件 contact_item.xml 会生成 ContactItemBinding 类。如果模块包是 com.example.my.app,则绑定类放在 com.example.my.app.databinding 包中。
通过调整 data 元素的 class 特性,绑定类可重命名或放置在不同的包中。例如,以下布局在当前模块的 databinding 包中生成 ContactItem 绑定类:
<data class="ContactItem">
…
</data>
您可以在类名前添加句点和前缀,从而在其他文件包中生成绑定类。以下示例在模块包中生成绑定类:
<data class=".ContactItem">
…
</data>
您还可以使用完整软件包名称来生成绑定类。以下示例在 com.example 包中创建 ContactItem 绑定类:
<data class="com.example.ContactItem">
…
</data>