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
    }
}

为什么要用 _bindingbinding 两个变量?

这是 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:同样需要在 onDestroyViewdismiss 时清理 binding。

复用场景:如果你在同一个 Activity 里多次 inflate 同一个布局(比如动态添加多个相同布局),每次 inflate 都会生成一个新的 binding 对象。这些对象之间互不干扰,各自管理自己的视图树。但要注意,不要把一个 binding 对象赋值给多个视图,否则会出现视图状态混乱的问题。

2.7 知识体系总览

下面这张图总结了 ViewBinding 在三个核心场景下的使用要点:

ViewBinding 核心使用场景 Activity Fragment Adapter 使用要点 • inflate + setContentView • lateinit var 声明 • 无需手动清理 • 生命周期跟随 Activity • 适合简单页面 • 代码最简洁 使用要点 • inflate + return root • _binding + binding 双变量 • onDestroyView 置 null • 防止内存泄漏 • 生命周期比 View 长 • 必须手动清理 使用要点 • ViewHolder 持有 binding • inflate 在 onCreateViewHolder • 直接访问视图属性 • 点击事件在 init 中设置 • 回收时自动清理 • 代码最整洁 核心原则:用完后及时释放,避免内存泄漏

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