视图绑定的发展历史
如上图所示,视图绑定的发展历史经过了 findViewById --> ButterKnife --> Kotlin的扩展插件 --> ViewBinding 的变化。
findViewById 是原生获取对应View的接口,缺点是View的获取和绑定太麻烦。因此出现了很多框架来解决这个问题。最开始是 ButterKnife,它通过 APT 运行时注解生成的方式获取 View,但是它对组件化的支持不友好,目前该框架已经不再维护。
在 Google 主推 Kotlin 后,视图绑定一般会使用 Kotlin 的扩展插件,但是Kotlin 的扩展插件无法跨模块操作,对不同的资源文件存在相同id时,在引用来源时可能出错。在1.4版本该插件就被废弃了,目前推荐的就是这篇文章讲的 ViewBinding 了。
ViewBinding 会在编译时扫描 layout 文件生成对应的 ViewBinding 绑定类。它的优势是确保了获取对应 View 的空安全和类型安全。
ViewBinding 的使用
ViewBinding 的使用很简单,只需要在 build.gradle 中设置启用 viewBinding 就可以了。代码如下:
android {
...
viewBinding {
enabled = true
}
...
}
在配置完成后,该模块中的每个 layout 文件都会生成一个对应的绑定类。该绑定类的命名就是 layou 文件的名称转换为驼峰形式,并在末尾添加 Binding 一词。以 activity_main.xml 为例,其对应的绑定类名称为 ActivityMainBinding。
生成的绑定类一般可以在 build/generated/data_binding_base_class_source_out/debug/out 目录下的 your/package/name/databinding 中找到
启用 ViewBinding 功能的配置会为整个模块的所有布局文件生成对应的绑定类。如果某个布局文件不需要的话,可以使用 tools:viewBinding-Ignore
属性来设置,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:viewBinding-Ignore="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>
除此之外,我们还可以使用 tools:viewBindingType
属性来设置绑定的类型,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/edit_text"
tools:viewBindingType="android.widget.TextView" //设置 editText 类型为 TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
ViewBinding 的注意事项
在 Fragment 中使用 ViewBinding 时,需要注意的一点是,开发者需要在 onDestroyView 方法中将绑定类实例赋值为 null。这是因为 Fragment 的存在时间比其视图时间长,所以开发者需要在 onDestroyView 方法中清除对绑定类实例的所有引用,否则可能存在内存泄漏的风险。代码示例如下:
private var _binding: ResultProfileBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
不过这种通过人工的方式来设置为 null 太麻烦了,很多人可能一下子就忘了。要解决这个问题,我可以使用 Kotlin 的委托,代码示例如下所示。
class FragmentViewBindingDelegate<T : ViewBinding>(
val fragment: Fragment,
val viewBindingFactory: (View) -> T
) : ReadOnlyProperty<Fragment, T> {
private var binding: T? = null
init {
fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
val viewLifecycleOwnerLiveDataObserver =
Observer<LifecycleOwner?> {
val viewLifecycleOwner = it ?: return@Observer
viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onDestroy(owner: LifecycleOwner) {
binding = null
}
})
}
override fun onCreate(owner: LifecycleOwner) {
fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerLiveDataObserver)
}
override fun onDestroy(owner: LifecycleOwner) {
fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerLiveDataObserver)
}
})
}
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
val binding = binding
if (binding != null) {
return binding
}
val lifecycle = fragment.viewLifecycleOwner.lifecycle
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.")
}
return viewBindingFactory(thisRef.requireView()).also { this.binding = it }
}
}
//Fragment 扩展方法 viewBinding
fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
FragmentViewBindingDelegate(this, viewBindingFactory)
class MyFragment: Fragment() {
val binding by viewBinding(FragmentMyBinding::bind)
}
这样我们就可以通过 viewBinding
扩展方法来处理 ViewBinding 的置 null 了,避免了手动操作时程序员忘记的问题。
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)
PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题