文章首發於公衆號
「 技術最TOP 」
ViewBinding 是Android Studio 3.6中添加的一個新功能,更準確的說,它是DataBinding 的一個更輕量變體,爲何要使用View Binding 呢?答案是性能。許多開發者使用Data Binding庫來引用Layout XML中的視圖,而忽略它的其餘強大功能。相比來講,自動生成代碼ViewBinding其實比DataBinding 性能更好。可是傳統的方式使用View Binding 卻不是很好,由於會有不少樣板代碼(垃圾代碼)。java
讓咱們看看Fragment 中「ViewBinding」的用法。咱們有一個佈局資源profile.xml
。View Binding 爲佈局文件生成的類叫ProfileBinding
,傳統使用方式以下:git
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 } }
有幾點我不太喜歡:github
viewBinding
的樣板代碼viewBinding
屬性是可空的,而且可變的,這可不太妙怎麼辦呢?用強大Kotlin來重構它。ide
使用Kotlin委託的屬性,咱們能夠重用部分代碼並簡化任務(不明白委託屬性的,能夠看我(譯者)之前的文章:一文完全搞懂Kotlin中的委託),我用它來簡化·ViewBinding的用法。用一個委託包裝了
ViewBinding`的建立和銷燬。oop
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)) }
而後,使用咱們定義的委託來重構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 } }
很好,咱們去掉了建立和銷燬ViewBinding的樣板代碼,如今只須要聲明一個委託屬性就能夠了,是否是簡單了?可是如今還有點問題。post
在重構以後,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 } }
可是,結果是,我獲得的在委託屬性內對ViewBinding的引用爲null
。緣由是Fragment的ViewLifecycleOwner
通知更新lifecycle的ON_DESTROY
事件時機,該事件發生在Fragment.onDestroyView()
以前。這就是爲何我僅在主線程上的全部操做完成後才須要清除viewBinding。可使用Handler.post
完成。修改以下:ui
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 } } } }
這樣,就很完美了。this
Android的新庫ViewBinding是一個去掉項目中findViewByid()
很好的解決方案,同時它也替代了著名的Butter Knife
。ViewBinding 與Kotlin委託屬性的巧妙結合,可讓你的代碼更加簡潔易讀。完整的代碼能夠查看github:https://github.com/kirich1409...