Skip to content

Instantly share code, notes, and snippets.

@Zhuinden
Last active February 24, 2024 20:13
Show Gist options
  • Star 71 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save Zhuinden/ea3189198938bd16c03db628e084a4fa to your computer and use it in GitHub Desktop.
Save Zhuinden/ea3189198938bd16c03db628e084a4fa to your computer and use it in GitHub Desktop.
Fragment view binding delegate
// 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
Copy link

Drjacky commented Jan 12, 2022

@flamesoft But we have setContentView(binding.root) in onCreate for Activity and = binding.root in onCreateView

@flamesoft
Copy link

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

@AndroidDeveloperLB
Copy link

How would I use this in DialogFragment?

@Zhuinden
Copy link
Author

I just don't use this in a DialogFragment.

@AndroidDeveloperLB
Copy link

@Zhuinden Isn't it possible to add support for it, or have a similar solution for it?

@gmk57
Copy link

gmk57 commented Dec 23, 2022

@AndroidDeveloperLB There are two "flavors" of DialogFragment, they have different view lifecycle. You can find examples for both cases here.

@AndroidDeveloperLB
Copy link

AndroidDeveloperLB commented Dec 24, 2022

@gmk57 Oh these make sense. Thank you!

@Zhuinden
Copy link
Author

Zhuinden commented Feb 16, 2023

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.

Btw there is an updated version of this gist as a library in https://github.com/Zhuinden/fragmentviewbindingdelegate-kt

@manju23reddy
Copy link

manju23reddy commented Apr 13, 2023

@Zhuinden what is the real reason behind val binding = binding in most cases we directly access binding. is this required ? .

@Zhuinden
Copy link
Author

Zhuinden commented Apr 13, 2023

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment