• 赚钱入口【需求资源】限时招募流量主、渠道主,站长合作;【合作模式】CPS长期分成,一次推广永久有收益。主动打款,不扣量;

Fragments中的ViewBinding

Android rin, seun 1年前 (2020-06-21) 420次浏览 0个评论

使用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"
    }
}
  1. ViewBindingHolder<YourBindingClass> by…ViewBindingHolder类初始化为Fragment的委托。
  2. 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
    }
}

喜欢 (0)

您必须 登录 才能发表评论!