比 kotlin-android-extensions
插件更好用的 ViewBinding
前言
因为今年工做比较忙,很久没更新文章了,几年作的项目比较多,最近会陆陆续续记录一下今年遇到的问题或知识。android
1、kotlin-android-extensions
存在的问题
一、污染全局命名空间安全
二、不能暴露可空性信息app
三、仅支持 Kotlin 代码ide
这里有篇文章有说明,具体能够参考这里:https://www.bennyhuo.com/2020/11/07/deprecated-kotlin-android-extensions/布局
注意:根据谷歌的将来计划,在接下来的一年里,谷歌的团队将共同弃用 synthetics,并继续支持——“视图绑定 (View Binding)”post
具体说明能够看这里:https://mp.weixin.qq.com/s/pa1YOFA1snTMYhrjnWqIgggradle
2、ViewBinding
的使用
一、设置说明
将 viewBinding
元素添加到其 build.gradle
文件中,以下例所示:ui
android { ... viewBinding { enabled = true } }
若是要忽略某个布局文件,请将 tools:viewBindingIgnore="true"
属性添加到相应布局文件的根视图中:spa
<LinearLayout ... tools:viewBindingIgnore="true" > ... </LinearLayout>
二、用法
启用视图绑定功能后,系统会为该模块中包含的每一个 XML 布局文件生成一个绑定类。每一个绑定类均包含对根视图以及具备 ID 的全部视图的引用。系统会经过如下方式生成绑定类的名称:将 XML 文件的名称转换为驼峰式大小写,并在末尾添加“Binding”一词。插件
例如,假设某个布局文件的名称为 result_profile.xml
:
<LinearLayout ... > <TextView android:id="@+id/name" /> <ImageView android:cropToPadding="true" /> <Button android:id="@+id/button" android:background="@drawable/rounded_button" /> </LinearLayout>
所生成的绑定类的名称就为 ResultProfileBinding
。此类具备两个字段:一个是名为 name
的 TextView
,另外一个是名为 button
的 Button
。该布局中的 ImageView
没有 ID,所以绑定类中不存在对它的引用。
每一个绑定类还包含一个 getRoot()
方法,用于为相应布局文件的根视图提供直接引用。在此示例中,ResultProfileBinding
类中的 getRoot()
方法会返回 LinearLayout
根视图。
如下几个部分介绍了生成的绑定类在 Activity 和 Fragment 中的使用。
在 Activity 中使用视图绑定
如需设置绑定类的实例以供 Activity 使用,请在 Activity 的 onCreate()
方法中执行如下步骤:
- 调用生成的绑定类中包含的静态
inflate()
方法。此操做会建立该绑定类的实例以供 Activity 使用。 - 经过调用
getRoot()
方法或使用Kotlin
属性语法获取对根视图的引用。 - 将根视图传递到
setContentView()
,使其成为屏幕上的活动视图。
private lateinit var binding: ResultProfileBinding override fun onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) binding = ResultProfileBinding.inflate(layoutInflater) val view = binding.root setContentView(view) }
您如今便可使用该绑定类的实例来引用任何视图:
binding.name.text = viewModel.name binding.button.setOnClickListener { viewModel.userClicked() }
在 Fragment 中使用视图绑定
如需设置绑定类的实例以供 Fragment 使用,请在 Fragment 的 onCreateView()
方法中执行如下步骤:
- 调用生成的绑定类中包含的静态
inflate()
方法。此操做会建立该绑定类的实例以供 Fragment 使用。 - 经过调用
getRoot()
方法或使用Kotlin
属性语法获取对根视图的引用。 - 从
onCreateView()
方法返回根视图,使其成为屏幕上的活动视图。
注意:inflate()
方法会要求您传入LayoutInflater
。若是布局已inflate
,您能够调用绑定类的静态 bind()
方法。
private var _binding: ResultProfileBinding? = null // This property is only valid between onCreateView and // onDestroyView. 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 }
您如今便可使用该绑定类的实例来引用任何视图:
binding.name.text = viewModel.name binding.button.setOnClickListener { viewModel.userClicked() }
注意:Fragment 的存在时间比其视图长。请务必在 Fragment 的 onDestroyView()
方法中清除对绑定类实例的全部引用。
与 findViewById 的区别
- Null 安全
- 类型安全
与
DataBinding
的对比
- 更快的编译速度
- 易于使用
3、ViewBinding
的封装
一、activity中的封装
基类
abstract class BaseActivity<T : ViewBinding> : AppCompatActivity() { private var mBinding: T? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(getViewBinding().also { mBinding = it }.root) } abstract fun getViewBinding(): T override fun onDestroy() { super.onDestroy() mBinding = null } }
MainActivity
继承这个基类并实现getViewBinding
这个方法`
override fun getViewBinding(): ActivityMainBinding { return ActivityMainBinding.inflate(layoutInflater) }
二、fragment中的封装
基类
abstract class BaseFragment<T : ViewBinding> : Fragment() { private var mBaseBinding: T? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return onCreateBinding(inflater, container, savedInstanceState).also { mBaseBinding = it }.root } abstract fun onCreateBinding( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): T override fun onDestroyView() { super.onDestroyView() mBaseBinding = null } }
FirstFragment
继承基类并实现如下:
override fun onCreateBinding( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): FragmentEarnBinding { return FragmentFirstBinding.inflate(inflater, container, false) }
更好的封装能够参考这篇文章:https://juejin.cn/post/6906153878312452103
4、使用时的问题
在使用include引入布局时
使用bind将include所包含的布局引入进来;
layoutToolBarBinding = LayoutToolBarBinding.bind(ActivityMainBinding.inflate(layoutInflater().root());
在使用ViewStub
引入布局时
// mBinding是Activity或Fragment的viewbinding // customViewstub为 Viewstub的id mBinding.customViewstub.setOnInflateListener { stub, inflated -> run { //LayoutCustomBinding 为viewStuby所引用的布局生成的viewbinding LayoutCustomBinding.bind(inflated) } } mBinding.customViewstub.inflate()