2、ViewBinding基础:Activity中使用ViewBinding、Fragment中使用ViewBinding、Adapter中使用ViewBinding、Binding对象的复用与清理
ViewBinding 这东西,说白了就是帮我们省掉 findViewById 的重复劳动。我刚入行那会儿,一个 Activity 里写几十行 findViewById 是家常便饭,改个 ID 还得全局搜索替换,烦得很。后来 Google 推出了 ViewBinding,我才算真正解脱了。
这一章,我会带你走一遍 ViewBinding 在三个核心场景下的用法:Activity、Fragment、Adapter。每个场景都有它的“脾气”,不注意细节就容易翻车。嗯,咱们一个一个来看。
2.1 什么是 ViewBinding?
ViewBinding 是 Android 官方提供的一个视图绑定工具。它会在编译时为你生成一个 Binding 类,这个类里包含了布局文件中所有带 ID 的 View 的直接引用。你不需要再写 findViewById,直接通过 Binding 对象就能操作视图。
我个人习惯把 ViewBinding 理解为“布局文件的类型安全代理”。它比传统的 findViewById 更安全,也比 DataBinding 更轻量。如果你只是想做视图绑定,不想搞数据驱动,ViewBinding 就是最佳选择。
核心优势:
- 类型安全:编译时检查,不会出现类型转换异常
- 空安全:对
include标签的处理更智能 - 零性能损耗:没有反射,没有注解处理器
- 代码简洁:一行代码搞定所有视图绑定
2.2 启用 ViewBinding
在模块的 build.gradle 中启用:
android {
...
buildFeatures {
viewBinding = true
}
}
同步之后,每个布局文件都会自动生成一个对应的 Binding 类。比如 activity_main.xml 会生成 ActivityMainBinding。命名规则就是把布局文件名转成驼峰,然后加上 Binding 后缀。
2.3 Activity 中使用 ViewBinding
Activity 中使用 ViewBinding 是最简单的场景。直接上代码:
class MainActivity : AppCompatActivity() {
// 声明一个 lateinit 的 binding 变量
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 关键一步:通过 inflate 方法创建 binding 对象
binding = ActivityMainBinding.inflate(layoutInflater)
// 把 binding.root 作为 content view
setContentView(binding.root)
// 现在可以直接用 binding 访问视图了
binding.tvTitle.text = "Hello ViewBinding"
binding.btnSubmit.setOnClickListener {
// 处理点击事件
}
}
}
这里有个细节要注意:setContentView(binding.root) 这一步不能省。我曾经见过有人忘了这行,结果页面一片空白,找了半天才发现是 root 没设置上去。
我的习惯:把 binding 声明为 private lateinit var,这样既保证了封装性,又避免了空检查的麻烦。如果你在多个方法里都要用 binding,这个写法最省心。
2.4 Fragment 中使用 ViewBinding
Fragment 的生命周期比 Activity 复杂,所以 ViewBinding 的使用方式也略有不同。核心问题是:binding 对象什么时候创建,什么时候销毁。
class HomeFragment : Fragment() {
// 同样声明 lateinit
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 在 onCreateView 中 inflate
_binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 在这里操作视图
binding.tvTitle.text = "Fragment 中使用 ViewBinding"
}
override fun onDestroyView() {
super.onDestroyView()
// 关键:清理 binding 引用,防止内存泄漏
_binding = null
}
}
为什么要用 _binding 和 binding 两个变量?
这是 Fragment 的标准写法。因为 Fragment 的生命周期比 View 长,如果 View 被销毁了但 Fragment 还在,binding 对象还持有 View 引用就会造成内存泄漏。所以我们在 onDestroyView 里把 _binding 置为 null,而 binding 通过 getter 返回非空值,方便使用。
我曾经踩过的坑:在 Fragment 的 onCreateView 里直接返回了 binding.root,但忘记在 onDestroyView 里清理。结果页面切来切去,内存占用越来越高。后来用 LeakCanary 一查,全是 Fragment 的 View 泄漏。所以,onDestroyView 里置 null 这一步,千万别省。
2.5 Adapter 中使用 ViewBinding
Adapter 中使用 ViewBinding,主要是优化 ViewHolder 的写法。传统的 ViewHolder 要写 findViewById,现在直接用 Binding 类替代。
class UserAdapter(private val userList: List<User>) :
RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
// 通过 inflate 创建 binding
val binding = ItemUserBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return UserViewHolder(binding)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val user = userList[position]
// 直接通过 binding 访问视图
holder.binding.tvName.text = user.name
holder.binding.tvEmail.text = user.email
}
override fun getItemCount() = userList.size
// ViewHolder 持有 binding 对象
class UserViewHolder(val binding: ItemUserBinding) :
RecyclerView.ViewHolder(binding.root)
}
这里有个小技巧:ViewHolder 的构造函数直接接收 Binding 对象,而不是 View。这样在 onBindViewHolder 里就能直接通过 holder.binding 访问所有视图,代码非常干净。
我的建议:如果你在 Adapter 里需要处理点击事件,可以在 ViewHolder 的 init 块里设置监听器,避免在 onBindViewHolder 里重复创建匿名类。比如:
class UserViewHolder(val binding: ItemUserBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.btnDelete.setOnClickListener {
// 处理删除逻辑
}
}
}
2.6 Binding 对象的复用与清理
Binding 对象的生命周期管理,说白了就是一句话:用完了就释放,别让它活着占内存。
具体来说:
- Activity:Activity 销毁时,binding 对象自然跟着销毁,不需要手动清理。因为 Activity 的 View 树和 Activity 生命周期一致。
- Fragment:必须手动清理。在
onDestroyView里把_binding置为 null。这是 Fragment 和 Activity 最大的区别。 - Adapter:ViewHolder 被回收时,binding 对象也会被回收。但如果你在 ViewHolder 里持有了一些外部引用(比如 Context),要注意及时清理。
- Dialog 或 BottomSheet:同样需要在
onDestroyView或dismiss时清理 binding。
复用场景:如果你在同一个 Activity 里多次 inflate 同一个布局(比如动态添加多个相同布局),每次 inflate 都会生成一个新的 binding 对象。这些对象之间互不干扰,各自管理自己的视图树。但要注意,不要把一个 binding 对象赋值给多个视图,否则会出现视图状态混乱的问题。
2.7 知识体系总览
下面这张图总结了 ViewBinding 在三个核心场景下的使用要点:
2.8 避坑指南
最后,我把自己踩过的坑整理一下,你遇到了可以直接对照:
| 场景 | 常见错误 | 正确做法 |
|---|---|---|
| Activity | 忘记调用 setContentView(binding.root) | inflate 后立即 setContentView |
| Fragment | onDestroyView 中忘记置 null | 必须将 _binding 置为 null |
| Fragment | 在 onCreateView 之外使用 binding | 确保 View 已创建,在 onViewCreated 中操作 |
| Adapter | 在 onBindViewHolder 中重复创建监听器 | 在 ViewHolder 的 init 块中设置一次 |
| 所有场景 | 多个视图共享同一个 binding 对象 | 每个视图 inflate 自己的 binding |
ViewBinding 其实不复杂,核心就是三件事:inflate、使用、清理。Activity 最简单,Fragment 要小心,Adapter 最规整。把这三种场景的写法记牢了,以后写 UI 代码会顺手很多。
嗯,这一章就到这里。代码不多,但都是实战中反复验证过的写法。你照着写,不会出问题。
公众号:蓝海资料掘金营,微信deep3321