文章目录
-
- [1. 背景](#1. 背景)
- [2. ViewBinding是什么](#2. ViewBinding是什么)
- [3. 开启ViewBinding功能](#3. 开启ViewBinding功能)
- [4. 生成绑定类](#4. 生成绑定类)
- [5. 使用ViewBinding](#5. 使用ViewBinding)
-
- [5.1Activity 中使用](#5.1Activity 中使用)
- [5.2 Fragment 中使用](#5.2 Fragment 中使用)
- [5.3 ViewHolder 中使用](#5.3 ViewHolder 中使用)
- [6. ViewBinding的优点](#6. ViewBinding的优点)
- [7. 与 dataBinding 对比](#7. 与 dataBinding 对比)
1. 背景
写代码最繁琐的是什么?重复的机械操作。我们刚接触Android开发时最常写的操作肯定少不了findViewById 的身影。如果页面简单,负担还好,多写几行而已,但如果界面中存在几十上个View呢?再或者,重复的做一件枯燥的事几百次呢?这时候就会敲代码敲到手抽筋,有点生无可恋了吧。
2. ViewBinding是什么
这时候你的救星它来了!解放你的双手,效率提升十倍!它就是 ViewBinding !
ViewBinding ,顾名思义是"视图绑定"。它可以自动为 XML 布局文件生成一个绑定类,通过这个绑定类,你可以直接拿到布局中的View,再也不用 findViewById 的一个个去找了。
ViewBinding 是AndroidStudio3.6以后就支持的功能,现在大家的Android Studio版本应该都是202x.x.x这种新的年月日版本了吧。Android Studio4.2.2之后就采用这种新版本命名法了。
3. 开启ViewBinding功能
在 module级别的 build.gradle 文件中,添加如下代码:
groovy
android {
...
buildFeatures {
viewBinding true
}
}
如果你的 build.gradle 是 build.gradle.kts 这种文件,则这样添加代码:
groovy
android {
...
buildFeatures {
viewBinding = true
}
}
4. 生成绑定类
添加配置代码之后,会提示你点击 sync 同步代码,然后 build 一下工程 AS 会自动为你的工程生成绑定类代码,目录在app/build/generated/data_binding_xxx下。
生成的绑定类命名规则是,将 XML 文件的名称转换为"驼峰命名法"的形式,并在末尾添加"Binding"一词。比如,你的布局文件名是:activity_main.xml,那么生成的绑定类名是: ActivityMainBinding
默认情况下,AS会对工程中的所有xml文件生成绑定类。如果不想为某个布局文件生成,则可以将 tools:viewBindingIgnore="true" 属性添加到该布局文件的根视图中,例如:👇
xml
<LinearLayout
...
tools:viewBindingIgnore="true" >
...
</LinearLayout>
5. 使用ViewBinding
ViewBinding可以用在各种需要布局与代码交互的地方,如Activity、Fragment、ViewHolder等
5.1Activity 中使用
首先是布局,不需要做任何修改😄,比如布局:activity_main.xml👇:
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<ImageView
android:id="@+id/iv_icon"
android:layout_width="100dp"
android:layout_height="100dp" />
<Button
android:id="@+id/btn_ok"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="按钮" />
</LinearLayout>
-
使用 ViewBinding 前
MainActivity.kt 👇:
kotlinimport androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Button import android.widget.ImageView import android.widget.TextView class MainActivity : AppCompatActivity() { private var tvTitle: TextView? = null private var ivIcon: ImageView? = null private var btnOk: Button? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) tvTitle = findViewById(R.id.tv_title) ivIcon = findViewById(R.id.iv_icon) btnOk = findViewById(R.id.btn_ok) tvTitle?.text = "你好" ivIcon?.setBackgroundColor(Color.RED) btnOk?.text = "确认" btnOk?.setOnClickListener { Toast.makeText(this@MainActivity, "点击了", Toast.LENGTH_SHORT).show() } } }
-
使用 ViewBinding 后
MainActivity.kt 👇:
kotlinimport android.graphics.Color import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Toast import com.example.mtes.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { private var activityMainBinding: ActivityMainBinding? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) activityMainBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(activityMainBinding?.root) activityMainBinding?.tvTitle?.text = "你好" activityMainBinding?.ivIcon?.setBackgroundColor(Color.RED) activityMainBinding?.btnOk?.text = "确认" activityMainBinding?.btnOk?.setOnClickListener { Toast.makeText(this@MainActivity, "点击了", Toast.LENGTH_SHORT).show() } } }
到这里,相信你已经学会了 ViewBinding 的基本用法,体会到它的便捷了吧,再见了 findViewById 😄。
5.2 Fragment 中使用
这里就省略布局的举例了,跟Activity一样,拿到binding后,直接点出来你想访问的view即可。相信聪明的你一定能理解👋
- 使用 ViewBinding 前,MyFragment.kt 👇:
kotlin
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
class MyFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_my, container, false)
}
}
-
使用 ViewBinding 后,MyFragment.kt 👇:
kotlinimport android.os.Bundle import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.example.mtes.databinding.FragmentMyBinding class MyFragment : Fragment() { private var binding: FragmentMyBinding? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { binding = FragmentMyBinding.inflate(layoutInflater, container, false) val view = binding?.root return view } }
5.3 ViewHolder 中使用
列表也是我们常见的场景,使用 RecyclerView + Adapter + ViewHolder 实现列表,里面少不了对ViewHolder中的View的 findViewById。
比如列表item布局: layout_holder.xml 👇
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv_item"
/>
</LinearLayout>
-
使用ViewHolder前,MyAdapter.kt👇:
kotlinimport android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.recyclerview.widget.RecyclerView class MyAdapter: RecyclerView.Adapter<MyAdapter.MyViewHolder>() { private val dataList: List<String>? = null class MyViewHolder(val itemView: View) : RecyclerView.ViewHolder(itemView) { } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.layout_holder, parent, false) return MyViewHolder(view) } override fun getItemCount(): Int { return dataList?.size ?: 0 } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val tvText: TextView = holder.itemView.findViewById<TextView>(R.id.tv_item) tvText.text = "你好" } }
-
使用ViewHolder后,MyAdapter.kt👇:
kotlinimport android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.example.mtes.databinding.LayoutHolderBinding class MyAdapter: RecyclerView.Adapter<MyAdapter.MyViewHolder>() { private val dataList: List<String>? = null class MyViewHolder(val binding: LayoutHolderBinding) : RecyclerView.ViewHolder(binding.root) { } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val binding = LayoutHolderBinding.inflate(LayoutInflater.from(parent.context), parent, false) return MyViewHolder(binding) } override fun getItemCount(): Int { return dataList?.size ?: 0 } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { holder.binding.tvItem.text = "标题" } }
6. ViewBinding的优点
与使用 findViewById 相比,视图绑定具有一些很显著的优点:
- Null 安全
由于 ViewBinding 会创建对布局中的 view 的直接引用,因此不存在因 view ID 找不到而引发 null 指针异常的风险. - 类型安全
viewbinding 生成的属性类型和布局中的View类型是一致的,不存在之前findViewById后,类型转换的风险。
7. 与 dataBinding 对比
dataBinding 与 viewBinding 类似,也是可以生成绑定代码,但侧重点在于对于数据的赋值,相比 viewBinding 更复杂一些。因此如果只是想省略 findViewById ,那么推荐用 viewBinding 就好了,因为它有以下优势:
- 加快编译速度:视图绑定不需要处理注解,因此编译时间更短。
- 易于使用:视图绑定不需要特别标记的 XML 布局文件,因此在您的应用中采用的速度更快。在模块中启用视图绑定后,它会自动应用于该模块的所有布局。
另一方面,与 dataBinding 相比,viewBinding也有一些劣势:
- viewBinding 不支持布局变量或布局表达式,因此它不能用于直接从 XML 布局文件声明动态界面内容。
- viewBinding 不支持双向数据绑定。
最后,dataBinding 与 viewBinding 是可以同时使用的,在哪个页面用哪个,取决于你喽🤣。
怎么样,有了 ViewBinding,妈妈再也不用担心你的手指了,快去试试吧~
如果这篇文章对你有用,欢迎支持🙏
官网文档:https://developer.android.google.cn/topic/libraries/view-binding#groovy