简体   繁体   中英

initliaze and use viewbinding views by kotlin lazy delegate making view making view not inflate properly after first inflate

So i am migrating my code from Kotlin Synthetic to ViewBinding but when i write ViewBinding code its so much boilerplate like the first assign all views as lateinit var to use them in the whole class and then initialize these views in onViewCreated so then i use by lazy delegate to overcome that issue and everything work fines but when i try my app and it is single-activity-architecture based so when i go from Afragment to BFragment and then come back to AFragment some views like (toolbar not showing its title and drawer icons and menu) not inflated and i debug and realize that lazy delegate causing this because its giving first-time assigned value and also the variable is also immutable val so i thought may be this causing the issue so i code custom mutable lazy delegate but this doesn't help either i don't know what i am doing please suggest anything.

MutableLazyDelegate.kt

class MutableLazyDelegate<T>(val initializer: () -> T) : ReadWriteProperty<Any?, T> {

    private object UNINITIALIZED_VALUE

    private var value: Any? = UNINITIALIZED_VALUE

    @Suppress("UNCHECKED_CAST")
    override fun getValue(thisRef: Any?, property: KProperty<*>): T =
        if (value == UNINITIALIZED_VALUE)
            synchronized(this) {
                if (value == UNINITIALIZED_VALUE) initializer().also {
                    value = it
                } else value as T
            }
        else value as T

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        synchronized(this) {
            this.value = value
        }
    }
}

fun <T> mutableLazy(initializer: () -> T) = MutableLazyDelegate(initializer)

AFragment

class AFragment : Fragment(R.layout.a_fragment) {

    private val binding by viewBinding(AFragmentBinding::bind)

    private var toolbar by mutableLazy { binding.toolbar }

    ....
}

FragmentViewBindingDelegate

class FragmentViewBindingDelegate<T : ViewBinding>(
    val fragment: Fragment,
    val viewBindingFactory: (View) -> T
) : ReadOnlyProperty<Fragment, T> {

    var binding: T? = null

    init {
        fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
            val viewLifecycleOwnerLiveDataObserver =
                Observer<LifecycleOwner?> {
                    val viewLifecycleOwner = it ?: return@Observer
                    viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
                        override fun onDestroy(owner: LifecycleOwner) {
                            binding = null
                        }
                    })
                }

            override fun onCreate(owner: LifecycleOwner) {
                fragment.viewLifecycleOwnerLiveData.observeForever(
                    viewLifecycleOwnerLiveDataObserver
                )
            }

            override fun onDestroy(owner: LifecycleOwner) {
                fragment.viewLifecycleOwnerLiveData.removeObserver(
                    viewLifecycleOwnerLiveDataObserver
                )
            }
        })
    }

    override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
        val binding = binding
        if (binding != null) return binding
        val lifecycle = fragment.viewLifecycleOwner.lifecycle
        if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
            throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.")
        }
        return viewBindingFactory(thisRef.requireView()).also { this.binding = it }
    }
}

fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
    FragmentViewBindingDelegate(this, viewBindingFactory)

The solution in your case is to ditch the mutableLazy delegate and instead use an accessor

private val toolbar: Toolbar get() = binding.toolbar 

Although in most cases, I'd recommend grabbing the toolbar from the binding variable that is assigned to a val in the method where you're using it

override fun onViewCreated(...) {
    super onViewCreated(...) 
    val binding = binding

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM