Jetpack 是谷歌推出一套库、工具和指南,可以帮助开发者更轻松地编写优质应用,摆脱编写样板代码的工作并简化复杂任务,将精力集中放在所需的代码上。
简而言之,可以理解为是官方提供的一套 Android 开发脚手架,在这个新技术层出不群,质量也无法保证的时代,跟着官方的建议走,也未尝不是一件坏事。视图绑定属于 Jetpack 中的架构组件,主要就是用来快速方便的引用视图,精准定位,彻底帮你省去 findViewById
。
使用
ViewBinding 是在 Android Gradle 插件 3.6.0 版本中引入,因此要确保 Android Studio 和 Gradle 插件版本号大于等于 3.6.0。
接着,需要在模块的 build.gradle
文件下启用:
1 | android { |
Android Gradle 插件 4.0.0-alpha05 引入了buildFeatures
块来控制构建功能,如视图绑定、数据绑定和 Jetpack Compose,需要如下这样配置:
1 | android { |
此时,当创建一个 XML 布局文件后,就会以 XML 文件名称按照驼峰大小形式来自动生成一个绑定类,例如:activity_main.xml
对应的是 ActivityMainBinding.java
,activityuser.xml
对应 Activityuser.java
。
通过调用对应 Binding 类的 inflate()
方法,并传入参数 LayoutInflater 来获取一个实例。
1 | private lateinit var viewBinding : ActivityMainBinding |
可以通过 getRoot()
来获取布局的根View,这里 ViewBinding 还提供了接受三个参数的 inflate() 方法,与 LayoutInflater 的接口一致。在获取到实例后,就可以通过 binding.id 来获取到对应 ID 的 View 实例了。
1 | viewBinding.my_text.setText("hello, world.") |
默认情况下,会为每个布局 XML 文件都生成绑定类,可以通过将 tools:viewBindingIgnore="true"
属性添加到布局文件的根视图来忽略生成。
分析
既然 ViewBinding 的作用是用来减少写 findViewById
样板代码,那它就免不了 ButterKnife 做一下对比了,官方给出的优点是:
- Null 安全:由于视图绑定会创建对视图的直接引用,因此不存在因视图 ID 无效而引发 Null 指针异常的风险。此外,如果视图仅出现在布局的某些配置中,则绑定类中包含其引用的字段会使用
@Nullable
标记。 - 类型安全:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着不存在发生类转换异常的风险。
如下面的代码所示,在声明成员变量时,会根据 XML 文件中的 View 类型来创建,因此不会存在类型转换的问题,并且会添加注解 @NonNull
。
1 |
|
在 ViewBinding 的 inflate() 中,会去调用另一个静态方法 bind(),在这里,会通过 findViewById 来获取 View,如果为空,则会跑出异常,保证了在编译期间报错,而不是在运行期间。
更为关键的是,不用依赖任何库,这就大大减少了应用引入的成本。目前 ButterKnife
的 Github 主页上已经有了备注,建议考虑下是否替换为 ViewBinding。
Attention: Development on this tool is winding down. Please consider switching to view binding in the coming months.
使用时可以发现,每次编辑完 XML 文件,例如添加一个带 ID 的 View 后,在 Java 文件中就可以通过对应的绑定类调用到,这是由于 ViewBinding 的代码生成插件会优先在内存中创建一个该 XML 的 Binding 对象,并对其进行更新,而并非是在 Build 之后才去创建。
与 DataBinding 的区别
使用 DataBinding 和使用 ViewBinding 所生成的绑定类名称是一样的,通过绑定类中的注释来看,并非是使用同一编译器生成。
1 | // Generated by data binding compiler. Do not edit! |
ViewBinding 的使用场景很单一,就是用来简化样板代码,其绑定类是实现了androidx.viewbinding.ViewBinding
接口
1 | /** A type which binds the views in a layout XML to fields. */ |
而 DataBinding 的绑定类则是继承自 androidx.databinding.ViewDataBinding
,并且 ViewDataBinding
是实现了 ViewBinding
接口,也就是说通过 DataBinding 也可以实现绑定视图,但需要注意的是,DataBinding 仅处理使用 layout
代码创建的数据绑定布局,即如果在布局中有 include 标签,并设置了 ID,那么如果包含的布局是没有在 layout
中,那么是调用不到 include 布局中的 View的。举个例子:
如果只打开了 ViewBinding,此时有两个布局,test_main.xml
1 | <!--test_main.xml--> |
和 test_include.xml
1 | <?xml version="1.0" encoding="utf-8"?> |
那么此时是可以通过 binding.test_main_include.test_include_image
来获取到 test_main.xml
布局所包含的布局中的元素的,因为通过 binding.test_main_include
获取到的是 TestIncludeBinding
,但如果此时只是使用 DataBinding,并且只有 test_main.xml
布局是在 layout
中,则此时调用 binding.test_main_include
获取到的只是 test_include.xml
布局的根 View ConstraintLayout 的实例,而无法获取到它的子 View,此时必须将 test_include.xml
也放在 layout
中,才可以正常获取到 test_include.xml
中的其他 View,或者也可以同时打开 ViewBinding 和 DataBinding 来实现。