ViewBinding的目的只有一个,就是为了避免编写findViewById,要想使用ViewBinding需要注意两件事,第一,确保你的AndroidStudio是3.6或更高的版本。
一、准备工作
在你项目工程模块的build.gradle中加入以下配置:
android {
...
buildFeatures {
viewBinding true
}
}
这样准备工作就完成了,下面会从Activity、Fragment、Adapter、引入布局四个方面分别讨论ViewBinding的用法。
二、在Activity中使用ViewBinding
一旦启动了ViewBinding功能之后,Android Studio会自动为我们所编写的每一个布局文件都生成一个对应的Binding类。
Binding类的命名规则是将布局文件按驼峰方式重命名后,再加上Binding作为结尾,比如说activity_main.xml布局,那么与它对应的Binding类就是ActivityMainBinding。
1.不为指定布局生成对应Binding
<LinearLayout
xmlns:tools="http://schemas.android.com/tools"
...
tools:viewBindingIgnore="true">
...
</LinearLayout>
2.在Activity中使用ViewBinding来设置TextView组件的text
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.textView.text = "Hello"
}
}
首先调用activity_main.xml布局文件对应的Binding类的inflate()函数去加载该布局,inflate()函数接受一个LayoutInflater参数,在Activity中是可以直接获取到的。调用Binding类的getRoot()函数可以得到activity_main.xml中根元素的实例,调用getTextView()函数可以获得id为textView的元素实例。然后将根元素实例传到setContentView()函数中,这样Activity就可以成功显示activity_main.xml这个布局的内容了。
三、在Fragment中使用ViewBinding
假设我们有一个布局文件叫fragment_main.xml,启用ViewBinding功能之后,会生成一个与其对应的FragmentMainBinding类。
class MainFragment : Fragment() {
private var _binding: FragmentMainBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View {
_binding = FragmentMainBinding.inflater(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
调用FragmentMainBinding的inflater()函数去加载fragment_main.xml布局文件,由于我们是在onCreateView()函数中加载的布局,那么理应在onDestroyView()函数中对binding变量置空,从而保证binding变量有效生命周期是在onCreateView()函数和onDestroyView()函数之间。
四、在Adapter中使用ViewBinding
假设我们定义了fruit_item.xml来作为RecyclerView子项的布局。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp">
<ImageView
android:id="@+id/fruitImage"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp" />
<TextView
android:id="@+id/fruitName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="10dp" />
</LinearLayout>
然后编写如下RecyclerView Adapter来加载和显示这个子项布局:
class FruitAdapter(val fruitList: List<Fruit>) : RecyclerView.Adapter<FruitAdapter.ViewHolder>() {
inner class ViewHolder(binding: FruitItemBinding) : RecyclerView.ViewHolder(binding.root) {
val fruitImage: ImageView = binding.fruitImage
val fruitName: TextView = binding.fruitName
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = FruitItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val fruit = fruitList[position]
holder.fruitImage.setImageResource(fruit.imageId)
holder.fruitName.text = fruit.name
}
override fun getItemCount() = fruitList.size
}
首先,在onCreateViewHolder()函数中调用FruitItemBinding的inflate()函数去加载fruit_item.xml布局文件,这和ViewBinding在Fragment中的用法是一模一样的。
五、在引入布局中使用ViewBinding
1.在引入布局中使用ViewBinding——include
假设我们有如下titlebar.xml布局,希望作为通用布局引入到各布局当中:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:text="Back" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Title"
android:textSize="20sp" />
<Button
android:id="@+id/done"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="Done" />
</RelativeLayout>
在activity_main.xml中引入布局并给include标签加上id:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/titleBar"
layout="@layout/titlebar" />
...
</LinearLayout>
在MainActivity中引用titlebar.xml中定义的组件:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.titleBar.title.text = "Title"
binding.titleBar.back.setOnClickListener {
}
binding.titleBar.done.setOnClickListener {
}
}
}
2.在引入布局中使用ViewBinding——merge
merge与include最大的区别在于,使用merge标签引入的布局在某些情况下可以减少一层布局的嵌套,而更少的布局嵌套通常就意味着更高的效率。比如我们队titlebar.xml进行如下修改:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:id="@+id/back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:text="Back" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Title"
android:textSize="20sp" />
<Button
android:id="@+id/done"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="Done" />
</merge>
可以看到,这里最外层布局使用了merge标签,这就表示当有任何一个地方去include这个布局时,会将merge标签内包含的内容直接填充到include的位置,不会再添加任何额外的布局结构。
但是很遗憾,如果依旧给include加上id用来绑定merge中的组件的话会导致程序的崩溃,所以应该去掉id。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
layout="@layout/titlebar" />
</LinearLayout>
然后修改MainActivity中的代码,如下所示:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var titlebarBinding: TitlebarBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
titlebarBinding = TitlebarBinding.bind(binding.root)
setContentView(binding.root)
titlebarBinding.title.text = "Title"
titlebarBinding.back.setOnClickListener {
}
titlebarBinding.done.setOnClickListener {
}
}
}