DataBinding(三):布局绑定类
写好我们的DataBinding布局之后如何使用呢?如何初始化在<data />中定义的变量呢?
1、绑定布局
已经将原来的XML布局转换成DataBinding支持的数据绑定布局,接下来就是将它设置给我们的Activity、Fragment等需要加载布局的地方。
1.1、Activity中绑定布局
在使用DataBinding之前通过setContentView(int layoutResID)方法设置Activity布局:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_data_binding);
}
启用DataBinding后,有两种方式加载data binding layout布局:
①使用DataBinding库中的DataBindingUtil.setContentView()方法给当前Activity设置布局,返回自动生成的绑定类实例:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DataBindingLayoutBinding dataBindingBinding = DataBindingUtil.setContentView(this, R.layout.data_binding_layout);
}
②或者先通过生成绑定类的inflate()静态方法加载布局,再通过Activity的setContentView(View view)方法设置当前Activity的布局。看似这种方法没有显示的指定layout,实际上MainLayoutBinding对应的xml不就是我们要加载的布局吗?
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MainLayoutBinding dataBindingBinding = MainLayoutBinding.inflate(getLayoutInflater());
setContentView(dataBindingBinding.getRoot());
}
1.2、Fragment中绑定布局
在自定义Fragment的onCreateView()中一般通过DataBindingUtil的inflate()方法绑定布局:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
FragmentBlankBinding fragmentBlankBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_blank, container, false);
... ...
return fragmentBlankBinding.getRoot();
}
1.3、RecyclerView绑定布局
在RecyclerView等组件中通过DataBindingUtil.inflate()方法加载布局:
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
RecyclerViewItemBinding recyclerViewItemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
R.layout.recycler_view_item, parent, false);
//设置DataBinding值
... ...
return new ViewHolder(recyclerViewItemBinding);
}
1.4、未知类型的绑定
实际项目中List、RecyclerView很复杂,加载的布局在加载前可能是未知的,无法预知加载哪个布局,或者可以加载多种类型的布局。这时候可以先加载xml布局,在将其设置给ViewDataBinding通用的抽象类型变量:
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//LayoutInflater从xml数据绑定布局加载View
View view = inflater.inflate(R.layout.fragment_blank, container, false);
//将View设置给ViewDataBinding
ViewDataBinding fragmentBlankBinding = DataBindingUtil.bind(view);
//...
return view;
}
2、绑定类 extends ViewDataBinding
DataBinding根据data binding layout布局为我们自动生成对应的Binding类,在项目的build/generated/data_binding_base_class_source_out/debug/out/包名/databinding目录下。继承自ViewDataBinding抽象类,实现ViewBinding接口:
/** A type which binds the views in a layout XML to fields. */
public interface ViewBinding {
/**
* Returns the outermost {@link View} in the associated layout file. If this binding is for a
* {@code <merge>} layout, this will return the first view inside of the merge tag.
*/
@NonNull
View getRoot();
}
2.1、ViewDataBinding抽象类
ViewDataBinding抽象类是所有自动生成的绑定类的父类,实现了ViewBinding接口,并且提供一些通用的方法。
public abstract class ViewDataBinding extends BaseObservable implements ViewBinding {
/**
* The root View that this Binding is associated with.
*/
private final View mRoot;
@Override
public View getRoot() {
return mRoot;
}
public abstract boolean setVariable(int variableId, @Nullable Object value);
...
}
2.2、绑定类名称规则
类名称基于布局文件的名称:首字母大写的驼峰命名法并在末尾添加Binding后缀。比如布局文件名为 data_binding_layout.xml,因此生成的对应类为 DataBindingLayoutBinding,包含从布局属性到布局视图的所有绑定变量,并且知道如何为绑定表达式指定值。
还可以在<data />中用class指定生成的绑定类名:
<data class="MainLayoutBinding">
...
</data>
2.3、获取控件
布局中原来是通过findViewById获取其中的控件。现在在DataBinding布局中定义控件,要获取的控件必须要有id,否则无法获取到:
<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{name}" />
直接通过DataBinding布局对象就可以拿到其中的控件对象,无需再通过findViewById。对象名是id去除下划线的驼峰命名法:
TextView textView = dataBindingBinding.textView;
getRoot()方法是通用的方法,可以获取根布局View对象。
2.4、变量setter/getter
在布局中定义了属性如何初始化设置值呢?数据绑定库为布局中声明的每个变量生成访问器方法。比如在布局中定义了String类型的变量name:
<variable
name="name"
type="String" />
在自动生成的DataBinding对象中就会有对应的setter和getter方法:
//设置
dataBindingBinding.setName("");
//获取
dataBindingBinding.getName();
2.5、BR类
数据绑定库在模块包中生成一个名为BR的类,在项目的build/generated/ap_generated_sources/debug/out/androidx/databinding/library/baseAdapters/BR.java目录下,其中包含用于数据绑定的资源的 ID。
通过该BR中的ID,调用父类ViewDataBinding的setVariable(int variableId,Object value)方法同样可以设置指定ID资源的值:
dataBindingBinding.setVariable(BR.name, "Quibbler");
这种方法特别有用,尤其是对于变量无法预先知道的data binding layout。
3、其它
使用ViewDataBinding有一些注意点,以后遇到其它问题会继续补充,记录经验。
3.1、线程安全
以前我们知道在开发Android的时候,子线程中设置View数据是不安全的。但是ViewDataBinding以在后台线程中更改数据模型(这个模型不能是集合)。数据绑定会在求值过程中对每个变量/字段进行本地化,以避免出现并发问题。
参考资料:
Jetpack > Libraries > Databinding
Data Binding:Generated binding classes