前言
最近看到一篇使用Kotlin委托屬性來消除使用ViewBinding過程中樣板代碼的文章,覺得不錯(cuò),因此翻譯給大家,原文地址:
https://proandroiddev.com/make-android-view-binding-great-with-kotlin-b71dd9c87719
正文
ViewBinding 是Android Studio 3.6中添加的一個(gè)新功能,更準(zhǔn)確的說,它是DataBinding 的一個(gè)更輕量變體,為什么要使用View Binding 呢?答案是性能。許多開發(fā)者使用Data Binding庫來引用Layout XML中的視圖,而忽略它的其他強(qiáng)大功能。相比來說,自動(dòng)生成代碼ViewBinding其實(shí)比DataBinding 性能更好。但是傳統(tǒng)的方式使用View Binding 卻不是很好,因?yàn)闀?huì)有很多樣板代碼(垃圾代碼)。
View Binding 的傳統(tǒng)使用方式
讓我們看看Fragment 中“ViewBinding”的用法。我們有一個(gè)布局資源profile.xml。View Binding 為布局文件生成的類叫ProfileBinding,傳統(tǒng)使用方式如下:
class ProfileFragment : Fragment(R.layout.profile) {
private var viewBinding: ProfileBinding? = null
override fun onViewCreated(view: View, savedState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewBinding = ProfileBinding.bind(view)
// Use viewBinding
}
override fun onDestroyView() {
super.onDestroyView()
viewBinding = null
}
}
有幾點(diǎn)我不太喜歡:
- 創(chuàng)建和銷毀
viewBinding的樣板代碼 - 如果有很多Fragment,每一個(gè)都要拷貝一份相同的代碼
-
viewBinding屬性是可空的,并且可變的,這可不太妙
怎么辦呢?用強(qiáng)大Kotlin來重構(gòu)它。
Kotlin 委托屬性結(jié)合ViewBinding
使用Kotlin委托的屬性,我們可以重用部分代碼并簡化任務(wù)(不明白委托屬性的,可以看我(譯者)以前的文章:一文徹底搞懂Kotlin中的委托),我用它來簡化·ViewBinding的用法。用一個(gè)委托包裝了ViewBinding`的創(chuàng)建和銷毀。
class FragmentViewBindingProperty<T : ViewBinding>(
private val viewBinder: ViewBinder<T>
) : ReadOnlyProperty<Fragment, T> {
private var viewBinding: T? = null
private val lifecycleObserver = BindingLifecycleObserver()
@MainThread
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
checkIsMainThread()
this.viewBinding?.let { return it }
val view = thisRef.requireView()
thisRef.viewLifecycleOwner.lifecycle.addObserver(lifecycleObserver)
return viewBinder.bind(view).also { vb -> this.viewBinding = vb }
}
private inner class BindingLifecycleObserver : DefaultLifecycleObserver {
private val mainHandler = Handler(Looper.getMainLooper())
@MainThread
override fun onDestroy(owner: LifecycleOwner) {
owner.lifecycle.removeObserver(this)
viewBinding = null
}
}
}
/**
* Create new [ViewBinding] associated with the [Fragment][this]
*/
@Suppress("unused")
inline fun <reified T : ViewBinding> Fragment.viewBinding(): ReadOnlyProperty<Fragment, T> {
return FragmentViewBindingProperty(DefaultViewBinder(T::class.java))
}
然后,使用我們定義的委托來重構(gòu)ProfileFragment:
class ProfileFragment : Fragment(R.layout.profile) {
private val viewBinding: ProfileBinding by viewBinding()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Use viewBinding
}
}
很好,我們?nèi)サ袅藙?chuàng)建和銷毀ViewBinding的樣板代碼,現(xiàn)在只需要聲明一個(gè)委托屬性就可以了,是不是簡單了?但是現(xiàn)在還有點(diǎn)問題。
問題來了
在重構(gòu)之后,onDestroyView 需要清理掉viewBinding中的View。
class ProfileFragment() : Fragment(R.layout.profile) {
private val viewBinding: ProfileBinding by viewBinding()
override fun onDestroyView() {
super.onDestroyView()
// Clear data in views from viewBinding
// ViewBinding inside viewBinding is null
}
}
但是,結(jié)果是,我得到的在委托屬性內(nèi)對ViewBinding的引用為null。原因是Fragment的ViewLifecycleOwner通知更新lifecycle的ON_DESTROY事件時(shí)機(jī),該事件發(fā)生在Fragment.onDestroyView()之前。這就是為什么我僅在主線程上的所有操作完成后才需要清除viewBinding??梢允褂?code>Handler.post完成。修改如下:
class FragmentViewBindingProperty<T : ViewBinding>(
private val viewBinder: ViewBinder<T>
) : ReadOnlyProperty<Fragment, T> {
private var viewBinding: T? = null
private val lifecycleObserver = BindingLifecycleObserver()
@MainThread
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
checkIsMainThread()
this.viewBinding?.let { return it }
val view = thisRef.requireView()
thisRef.viewLifecycleOwner.lifecycle.addObserver(lifecycleObserver)
return viewBinder.bind(view).also { vb -> this.viewBinding = vb }
}
private inner class BindingLifecycleObserver : DefaultLifecycleObserver {
private val mainHandler = Handler(Looper.getMainLooper())
@MainThread
override fun onDestroy(owner: LifecycleOwner) {
owner.lifecycle.removeObserver(this)
// Fragment.viewLifecycleOwner call LifecycleObserver.onDestroy() before Fragment.onDestroyView().
// That's why we need to postpone reset of the viewBinding
mainHandler.post {
viewBinding = null
}
}
}
}
這樣,就很完美了。
Android的新庫ViewBinding是一個(gè)去掉項(xiàng)目中findViewByid()很好的解決方案,同時(shí)它也替代了著名的Butter Knife。ViewBinding 與Kotlin委托屬性的巧妙結(jié)合,可以讓你的代碼更加簡潔易讀。完整的代碼可以查看github:https://github.com/kirich1409/ViewBindingPropertyDelegate
每天都有干貨文章持續(xù)更新,可以微信搜索
「 技術(shù)最TOP 」第一時(shí)間閱讀,回復(fù)【思維導(dǎo)圖】【面試】【簡歷】有我準(zhǔn)備一些Android進(jìn)階路線、面試指導(dǎo)和簡歷模板送給你