ButterKnife 被弃用,ViewBinding 才是 findView 的未来?,秀出天际
类型安全:res 下的任何 id 都可以被访问,有可能因访问了非当前 Layout 下的 id 而出错
空安全:这主要体现在 Configuration 中的对应布局不全时,运行时可能出现 NPE
兼容性:只能在 kotlin 中使用,java 不友好
局限性:不能跨 module 使用
也正是这几个缺点导致了 KAE 的大溃败。随着 Google 对亲儿子 ViewBinding 的大力推广,KAE 最终也招架不住,只能缴械投降---Jetbrains 在官网宣布废弃 KAE,并推荐开发者使用 ViewBinding.
5.ViewBinding
到这里,以上提到的多种 findView 方案都已经被废弃,唯独只剩 Google 官方正在大力推广的 ViewBinding 组件。ViewBinding 是 Google 在 2019 年 I/O 大会上公布的一款 Android 视图绑定工具。它的使用方式有点类似 DataBinding,但相比 DataBinding,ViewBinding 是一个更轻量级、更纯粹的 findViewById 的替代方案。它具有以下几个优点:
类型安全: ViewBinding 会基于布局中的 View 生成类型正确的属性。比如,在布局中放入了一个 TextView ,视图绑定就会暴露出一个 TextView 类型的属性供开发中使用。
空安全:ViewBinding 会检测某个视图是不是只在一些配置下存在,并依据结果生成带有 @Nullable 注解的属性。所以即使在多种配置下定义的布局文件,视图绑定依然能够保证空安全。
ViewBinding 生成的绑定类是一个 Java 类,并且添加了 Kotlin 的注解,可以很好的支持 Java 和 Kotlin 两种编程语言。
同时,Google 官方还给出了一个 ViewBinding、ButterKnife 以及 KAE 的对比,如下图:
总而言之,到目前为止除了 ViewBinding 我们已经别无选择。那么不妨接下来详细探究下 ViewBinding 的使用方法。
二、ViewBinding 使用详解
1.开启 ViewBinding
Android Studio 对于 ViewBinding 的支持是从 3.6 版本开始的,AS 3.6 版本内置了 Gradle 插件。只需要在 build.gradle 中通过以下配置即可开启 ViewBinding:
android?{ buildFeatures?{ viewBinding?=?true } }
如果,你的项目存在多个模块,则需要在每个模块的 gradle 中添加上述配置。完成以上配置后 ViewBinding 会为所有布局文件自动生成对应的绑定类。且无须修改原有布局的 XML 文件,ViewBinding 会根据现有的布局自动完成所有工作。
2.在 Activity 中使用 ViewBinding
首先编写 activity_main.xml 的布局文件,如下:
`<?xml?version="1.0"?encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout?xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello?World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"?/>
</androidx.constraintlayout.widget.ConstraintLayout>
`
完成后 gradle 插件会自动生成一个名为 ActivityMainBinding 的 Java 类,在 Activity 中通过 ActivityMainBinding 获取 Binding 实例,如下:
override?fun?onCreate(savedInstanceState:?Bundle?)?{ super.onCreate(savedInstanceState) val?binding?=?ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.textView.text?=?"Hello?World" }
3.ViewBinding 与 include 标签
在项目开发中,通常我们会使用 include 标签来简化布局文件,那么在使用了 include 标签的布局文件中,应该如何使用 ViewBinding 呢?且看代码:
`//?activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout?xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<include
android:id="@+id/include"
layout="@layout/layout_include"?/>
</androidx.constraintlayout.widget.ConstraintLayout>
//?layout_include.xml
<androidx.constraintlayout.widget.ConstraintLayout?xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello?World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"?/>
</androidx.constraintlayout.widget.ConstraintLayout>
`
上述两个布局文件会分别生成 ActivityMainBinding 与 LayoutIncludeBinding 两个 Java 类,并且 ActivityMainBinding 类中通过组合依赖了 LayoutIncludeBinding 类。因此,使用方式如下:
override?fun?onCreate(savedInstanceState:?Bundle?)?{ super.onCreate(savedInstanceState) val?binding?=?ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) //??从ActivityMainBinding中获取LayoutIncludeBinding val?include?=?binding.include //?通过LayoutIncludeBinding为TextView赋值 include.tvText.text?=?"Hello?World" }
如果 layout_include.xml 文件位于子模块,经实践与以上代码的使用方式并无任何差异,但一定要在子模块中开启 ViewBinding 才行。
4.ViewBinding 在 Fragment 中的使用 在 Fragment 中使用 ViewBinding 与 Activity 中有些差异,这里为了简便,我们使用上述中的 activity_main.xml 作为 Fragment 的布局文件,则 Fragment 的代码如下:
`private?lateinit?var?binding:?ActivityMainBinding
override?fun?onCreateView(
inflater:?LayoutInflater,
container:?ViewGroup?,
savedInstanceState:?Bundle?
):?View??{
binding?=?ActivityMainBinding.inflate(inflater,?container,?false)
return?binding.root
}
override?fun?onViewCreated(view:?View,?savedInstanceState:?Bundle?)?{
super.onViewCreated(view,?savedInstanceState)
binding.textView.text="Hello?World"
}`
5.ViewBinding 在 RecyclerView#Adapter
中的使用
布局文件不再贴出,直接看 Adapter 的代码,如下所示:
`class?TestAdapter?:?RecyclerView.Adapter<TestViewHolder>()?{
override?fun?onCreateViewHolder(parent:?ViewGroup,?viewType:?Int):?TestViewHolder?{
val?binding?=
ItemTestBinding.inflate(LayoutInflater.from(parent.context))
return?TestViewHolder(binding)
}
override?fun?onBindViewHolder(holder:?TestViewHolder,?position:?Int)?{
holder.binding.textView.text?=?"Hello?World"
}
override?fun?getItemCount():?Int?{
return?10
}
class?TestViewHolder(var?binding:?ItemTestBinding)?:
RecyclerView.ViewHolder(binding.root)
}`
通过以上几个实例可以看到 ViewBinding 的使用是非常简单的。而 ViewBinding 的实现原理也并不难,Gradle 插件会根据布局文件在项目的 build 目录下生成相应的 ViewBinding 类,并且,最终也是通过 findViewById 来完成 View 的获取的。具体实现代码不再贴出,感兴趣的同学可以自行查看。
三、展望与总结
时代在发展,Android 获取 View 的方式仍在变化。ViewBinding 无疑是一个优秀的组件,但它真的是 Android 开发中获取 View 的最优方案吗?显然,并不是!因为 ViewBinding 归根结底还是通过 findViewById 实现,且需要插件生成相关的 Binding 类,虽然省去了手动编写,但是 ViewBinding 仍然没能解决代码冗余的问题。那什么才是 findViewById 的未来呢?大概最好的 findView 就是没有 findView 吧!目前 Google 正在朝着这一方向努力,正在开发的 Jetpack Compose 库就是要取代 Android 的布局文件,彻底消除 findViewById。相信在未来某一天,随着 Jetpack Compose 库的普及,这个旷日持久的 findViewById 之争也最终会画上一个圆满的句号。
评论