视图绑定在Android Studio 3.6 Canary 11 及更高版本中可用
配置viewBinding
android {
...
viewBinding {
enabled = true
}
}
如果希望生成绑定类忽略某个布局文件,可以添加如下属性:
<LinearLayout
...
tools:viewBindingIgnore="true" >
...
</LinearLayout>
使用viewBinding
绑定某个xml
布局文件以后会生成一个绑定类,每个绑定类均包含根视图以及包含ID所有视图的引用。系统会通过如下方式生成绑定类的名称:将xml
文件的名称转换为驼峰大小写,并在末尾添加Binding
一词。
比如:
<LinearLayout ... >
<TextView android:id="@+id/name" />
<ImageView android:cropToPadding="true" />
<Button android:id="@+id/button"/>
</LinearLayout>
假如当前布局文件名为activity_main
文件,则生成的绑定类名为ActivityMainBinding
。此类有两个布局ID的引用字段:一个是name
的TextView
,一个是button
的Button
,因为ImageView
没有设置id,所以此类没有它的引用。
ActivityMainBinding
包含一个getRoot()
方法,返回当前布局文件的根视图。通常情况下,还要调用setContentView()
方法,从而将该绑定的根视图作为参数进行传递,让它成为屏幕的活动视图:
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val inflater = LayoutInflater.from(this)
binding = ActivityMainBinding.inflate(inflater)
setContentView(binding.root)
}
与findViewById的优点:
Null安全:由于视图绑定会创建对视图的直接引用,因此不存在因为视图ID无效引发的
null
指针。此外,如果视图仅出现在布局的某些配置中,则绑定类中包含其引用的字段会使用@Nullable
标记。
类型安全:每个绑定类中的字段均具有他们在
xml
文件中引用的视图相匹配的类型。这意味着不存在发生类型转换异常的风险。
解析生成类
viewBinding
会根据当前的xml
布局生成类,类放在build
文件下的generated
中的data_binding_base_class_source_out
文件下。
调用ViewBinding
的inflate
方法:
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_main, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
在inflate
方法中,会根据直接绑定该布局文件,然后进行bind
方法:
@NonNull
public static ActivityMainBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
String missingId;
missingId: {
TextView tv = rootView.findViewById(R.id.tv);
if (tv == null) {
missingId = "tv";
break missingId;
}
return new ActivityMainBinding((ConstraintLayout) rootView, tv);
}
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
然后再bind
方法中,会根据布局中控件的id来找到当前的控件。然后执行生成类的构造方法:
private ActivityMainBinding(@NonNull ConstraintLayout rootView, @NonNull TextView tv) {
this.rootView = rootView;
this.tv = tv;
}