使用ViewBinding(可从Android Studio 3.6获得)时,最佳做法是使 Fragment中的绑定无效onDestroyView
。在我们的项目中,我们经常忘记这样做。我们还将复制的绑定逻辑复制粘贴到所有的Fragments中,从而导致代码重复,并在Fragments的方法中添加了大量的空检查。为了清理代码,我们创建了一个ViewBindingHolder
类,该类负责绑定的生命周期并提供一些帮助程序方法,以使片段中的代码安全且简短。
这种方法的优点
- 启用对绑定属性的安全访问。
- 使代码简洁明了。
- 在生命周期结束时使绑定无效。
用法
使用ViewBindingHolder
需要对Fragment进行两项调整:
class ExampleFragment : Fragment(), ViewBindingHolder<FragmentExampleBinding> by ViewBindingHolderImpl() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? = initBinding(FragmentExampleBinding.inflate(layoutInflater), this) { // do your onCreateView logic here, where 'this' is the binding object. textTitle.text = "Clean ViewBinding and Lifecycle handling" } }
ViewBindingHolder<YourBindingClass> by…
将ViewBindingHolder
类初始化为Fragment的委托。initBinding
在onCreateView中绑定视图。
在onCreateView之外,有两种访问绑定的方法:
1. binding ?
假定绑定可能null
是最安全的选择。由于我们使用委托,因此您可以直接从Fragment访问binding属性。
binding?.textTitle.text = “The safest way to access”
2. requireBinding()
当确定逻辑在片段的视图生命周期内运行时,可以使用requireBinding()
。
fun initTitle() = requireBinding() { // here, 'this' is the 'binding' property. textTitle.text = "ViewBinding and Lifecycle handling" }
您也可以在没有代码块的情况下使用它:
fun initTitle() { val binding = requireBinding() binding.textTitle.text = "ViewBinding and Lifecycle handling" }
在requireBinding()
Fragment的视图生命周期之外调用时,将抛出IllegalStateException,类似于的行为requireActivity()
。
Inside ViewBindingHolder
源码实现,详细参见注视
/** * Holds and manages ViewBinding inside a Fragment. */ interface ViewBindingHolder<T : ViewBinding> { val binding: T? /** * Saves the binding for cleanup on onDestroy, calls the specified function [onBound] with `this` value * as its receiver and returns the bound view root. */ fun initBinding(binding: T, fragment: Fragment, onBound: (T.() -> Unit)?): View /** * Calls the specified [block] with the binding as `this` value and returns the binding. As a consequence, this method * can be used with a code block lambda in [block] or to initialize a variable with the return type. * * @throws IllegalStateException if not currently holding a ViewBinding (when called outside of an active fragment's lifecycle) */ fun requireBinding(block: (T.() -> Unit)? = null): T } class ViewBindingHolderImpl<T : ViewBinding> : ViewBindingHolder<T>, LifecycleObserver { override var binding: T? = null var lifecycle: Lifecycle? = null private lateinit var fragmentName: String /** * To not leak memory we nullify the binding when the view is destroyed. */ @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroyView() { lifecycle?.removeObserver(this) // not mandatory, but preferred lifecycle = null binding = null } override fun requireBinding(block: (T.() -> Unit)?) = binding?.apply { block?.invoke(this) } ?: throw IllegalStateException("Accessing binding outside of Fragment lifecycle: $fragmentName") override fun initBinding(binding: T, fragment: Fragment, onBound: (T.() -> Unit)?): View { this.binding = binding lifecycle = fragment.viewLifecycleOwner.lifecycle lifecycle?.addObserver(this) fragmentName = fragment::class.simpleName ?: "N/A" onBound?.invoke(binding) return binding.root } }