// https://github.com/Zhuinden/fragmentviewbindingdelegate-kt | |
import android.view.View | |
import androidx.fragment.app.Fragment | |
import androidx.lifecycle.DefaultLifecycleObserver | |
import androidx.lifecycle.Lifecycle | |
import androidx.lifecycle.LifecycleOwner | |
import androidx.lifecycle.Observer | |
import androidx.viewbinding.ViewBinding | |
import kotlin.properties.ReadOnlyProperty | |
import kotlin.reflect.KProperty | |
class FragmentViewBindingDelegate<T : ViewBinding>( | |
val fragment: Fragment, | |
val viewBindingFactory: (View) -> T | |
) : ReadOnlyProperty<Fragment, T> { | |
private 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) |
@Drjacky sounds like you are missing val binding = binding
in your onViewCreated
OR you are actually accessing the view after onDestroyView
and potentially have a memory leak in your app anyway.
@Zhuinden You mean just this:
override fun onViewCreated(view: View, savedInstanceState: Bundle) {
super.onViewCreated(view, savedInstanceState)
val binding = binding
}
But still using the binding
from the top variable in lines that are outside of the onViewCreated
:
class FragBlah : Fragment() {
private val binding by viewInflateBinding(FragBlahBinding::inflate)
...
private fun visibilityConnectingContainer() {
binding.txtTitle.visibilityOff() //this binding is the one from the top class variable
}
?
In that case, the problem is that you have some listener that is called even after onDestroyView.
That's not a bug in this code.
@Drjacky I see that you forgot to add the layout name in the class FragBlah : Fragment() . It should be class FragBlah : Fragment(R.layout.your_layout_name).
@flamesoft But we have setContentView(binding.root)
in onCreate
for Activity and = binding.root
in onCreateView
I haven't tried this. I have just tested it with fragments and that works. Try to use fragment and see if it works. @Drjacky
How would I use this in DialogFragment?
I just don't use this in a DialogFragment.
@Zhuinden Isn't it possible to add support for it, or have a similar solution for it?
@AndroidDeveloperLB There are two "flavors" of DialogFragment
, they have different view lifecycle. You can find examples for both cases here.
@gmk57 Oh these make sense. Thank you!
sounds like you are missing
val binding = binding
in youronViewCreated
OR you are actually accessing the view afteronDestroyView
and potentially have a memory leak in your app anyway.
Btw there is an updated version of this gist as a library in https://github.com/Zhuinden/fragmentviewbindingdelegate-kt
@Zhuinden what is the real reason behind val binding = binding
in most cases we directly access binding. is this required ? .
@manju23reddy I debugged a guy's code who had trouble with callbacks of a WebView running on a different thread and this was the fix, so I do do it in my code personally.
@ZacSweers
And my delegate: