6、ViewBinding:告别findViewById,拥抱类型安全
说实话,每次看到项目里满屏的 findViewById,我都觉得回到了刀耕火种的年代。你想想看,一个简单的 TextView 赋值,要写三行代码:声明变量、findViewById、强转。烦不烦?
好在 Google 终于给了我们一个体面的解决方案——ViewBinding。这东西说白了,就是帮你自动生成绑定类,让你直接通过对象点出控件。嗯,今天我们就来好好聊聊它。
核心观点:ViewBinding 不是银弹,但它能让你告别 90% 的 findViewById 调用。我个人习惯是,新项目直接上 ViewBinding,老项目逐步迁移。
6.1 ViewBinding 配置:三步走
配置其实很简单,就三步。我在项目中遇到过很多新手卡在第一步——没开对开关。
- 打开模块级 build.gradle,在 android 块里加上:
android {
...
buildFeatures {
viewBinding = true
}
}
注意,是 viewBinding 不是 dataBinding。我曾经见过有人把这两个搞混,结果编译报错半天找不到原因。
- 同步项目。Gradle 同步后,系统会自动为每个布局文件生成一个 Binding 类。比如
activity_main.xml会生成ActivityMainBinding。 - 在 Activity 中使用:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 直接使用控件
binding.tvTitle.text = "Hello ViewBinding"
binding.btnSubmit.setOnClickListener { ... }
}
}
看到了吗?没有 findViewById,没有强转。类型安全,空安全,代码瞬间清爽了。
小提示:如果你某个布局不想生成 Binding 类,可以在根布局加上 tools:viewBindingIgnore="true"。不过我个人很少用这个,除非是纯工具布局。
6.2 ViewBinding vs DataBinding:别选错了
很多人问我:ViewBinding 和 DataBinding 到底选哪个?我的回答是:看需求。
DataBinding 能做 ViewBinding 的所有事,但反过来不行。DataBinding 支持双向绑定、表达式、数据驱动 UI。而 ViewBinding 只做一件事——帮你省掉 findViewById。
我画了张对比图,你一看就明白:
我的建议很直接:
- 如果你只需要替代 findViewById,选 ViewBinding。轻量、简单、编译快。
- 如果你要做 MVVM、数据驱动 UI、双向绑定,选 DataBinding。但要做好心理准备,调试起来会麻烦一点。
避坑指南:我曾经在一个项目里混用 ViewBinding 和 DataBinding,结果导致同一个布局生成了两个绑定类,编译冲突。后来我统一用 DataBinding 了。记住:一个模块里最好只选一种。
6.3 Fragment 中使用 ViewBinding:注意生命周期
Fragment 和 Activity 不一样。Activity 的 binding 可以在 onCreate 里直接 inflate 并 setContentView。但 Fragment 有 onCreateView 和 onDestroyView,生命周期更复杂。
我见过很多人直接在 Fragment 里用 Activity 的方式写,结果导致内存泄漏。嗯,这里要注意:binding 对象必须在 onDestroyView 里置空。
标准写法是这样的:
class MyFragment : Fragment() {
private var _binding: FragmentMyBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentMyBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 在这里使用 binding
binding.tvTitle.text = "Hello from Fragment"
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null // 防止内存泄漏
}
}
为什么用 _binding 和 binding 两个变量?这是 Kotlin 的惯用写法。_binding 是可空的,binding 是非空的。当 Fragment 的视图被销毁时,我们把 _binding 置空,避免持有已销毁的视图引用。
个人经验:我习惯在 onDestroyView 里把 _binding 置空,但如果你用了 Navigation 组件,要注意 Fragment 可能被重建但视图不重建的情况。这时候 binding 可能为 null,调用前最好判空。
6.4 实战:用 ViewBinding 简化 findViewById
好了,理论说完了,我们来个实战。假设你有一个登录界面,里面有用户名输入框、密码输入框、登录按钮。传统写法是这样的:
// 传统写法
class LoginActivity : AppCompatActivity() {
private lateinit var etUsername: EditText
private lateinit var etPassword: EditText
private lateinit var btnLogin: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
etUsername = findViewById(R.id.et_username)
etPassword = findViewById(R.id.et_password)
btnLogin = findViewById(R.id.btn_login)
btnLogin.setOnClickListener {
val username = etUsername.text.toString()
val password = etPassword.text.toString()
// 登录逻辑
}
}
}
用 ViewBinding 改写后:
// ViewBinding 写法
class LoginActivity : AppCompatActivity() {
private lateinit var binding: ActivityLoginBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnLogin.setOnClickListener {
val username = binding.etUsername.text.toString()
val password = binding.etPassword.text.toString()
// 登录逻辑
}
}
}
代码量减少了将近一半。而且你再也不用担心 R.id 写错导致空指针了。因为 binding 里的控件都是根据布局文件自动生成的,类型完全匹配。
如果你有多个控件需要频繁操作,还可以用 with(binding) 进一步简化:
with(binding) {
btnLogin.setOnClickListener {
val username = etUsername.text.toString()
val password = etPassword.text.toString()
// ...
}
tvForgotPassword.setOnClickListener { ... }
cbRememberMe.setOnCheckedChangeListener { ... }
}
这样写,是不是感觉代码都变优雅了?
总结一下:ViewBinding 不是新技术,但它解决了 Android 开发中最基础也最烦人的问题。配置简单、使用方便、类型安全。如果你还没用上,我建议你从下一个模块开始尝试。相信我,你会爱上这种清爽的感觉。
公众号:蓝海资料掘金营,微信deep3321