最近看到一篇使用Kotlin委托属性来消除使用ViewBinding过程当中样板代码的文章,以为不错,所以翻译给你们,原文地址:java
proandroiddev.com/make-androi…android
ViewBinding 是Android Studio 3.6中添加的一个新功能,更准确的说,它是DataBinding 的一个更轻量变体,为何要使用View Binding 呢?答案是性能。许多开发者使用Data Binding库来引用Layout XML中的视图,而忽略它的其余强大功能。相比来讲,自动生成代码ViewBinding其实比DataBinding 性能更好。可是传统的方式使用View Binding 却不是很好,由于会有不少样板代码(垃圾代码)。git
让咱们看看Fragment 中“ViewBinding”的用法。咱们有一个布局资源profile.xml
。View Binding 为布局文件生成的类叫ProfileBinding
,传统使用方式以下:github
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
}
}
复制代码
有几点我不太喜欢:ide
viewBinding
的样板代码viewBinding
属性是可空的,而且可变的,这可不太妙怎么办呢?用强大Kotlin来重构它。oop
使用Kotlin委托的属性,咱们能够重用部分代码并简化任务(不明白委托属性的,能够看我(译者)之前的文章:一文完全搞懂Kotlin中的委托),我用它来简化·ViewBinding的用法。用一个委托包装了
ViewBinding`的建立和销毁。布局
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
:post
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的样板代码,如今只须要声明一个委托属性就能够了,是否是简单了?可是如今还有点问题。性能
在重构以后,onDestroyView
须要清理掉viewBinding中的View。ui
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
完成。修改以下:
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是一个去掉项目中findViewByid()
很好的解决方案,同时它也替代了著名的Butter Knife
。ViewBinding 与Kotlin委托属性的巧妙结合,可让你的代码更加简洁易读。完整的代码能够查看github:github.com/kirich1409/…