kotlin-android-extensions
插件更好用的
ViewBinding
由于今年工作比较忙,好久没更新文章了,最近使用kotlin-android-extensions
遇到坑记录一下。
kotlin-android-extensions
存在的问题1、污染全局命名空间
2、不能暴露可空性信息
3、仅支持 Kotlin 代码
这里有篇文章有说明,具体可以参考这里:https://www.bennyhuo.com/2020/11/07/deprecated-kotlin-android-extensions/
注意:根据谷歌的未来计划,在接下来的一年里,谷歌的团队将共同弃用 synthetics,并继续支持——“视图绑定 (View Binding)”
具体说明可以看这里:https://mp.weixin.qq.com/s/pa1YOFA1snTMYhrjnWqIgg
ViewBinding
的使用将 viewBinding
元素添加到其 build.gradle
文件中,如下例所示:
android {
...
viewBinding {
enabled = true
}
}
如果要忽略某个布局文件,请将 tools:viewBindingIgnore="true"
属性添加到相应布局文件的根视图中:
<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 的 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 的 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
的对比
- 更快的编译速度
- 易于使用
ViewBinding
的封装基类
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)
}
基类
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
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()