比 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()