Skip to content

Instantly share code, notes, and snippets.

@Frank1234
Last active December 14, 2020 11:36
Show Gist options
  • Save Frank1234/94211000cae3ec7652442bbc18172f25 to your computer and use it in GitHub Desktop.
Save Frank1234/94211000cae3ec7652442bbc18172f25 to your computer and use it in GitHub Desktop.
Holds and manages ViewBinding inside a Fragment.
/**
* 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
}
}
@root-ansh
Copy link

Hey, when and how is the optional onDestroyView called ? Is it called automatically by the system when onDestroy() is triggerred ? In What sequence would this be getting called, when we have an onDestroyView already overridden in our fragment?

@Frank1234
Copy link
Author

Frank1234 commented Dec 14, 2020

Hey, when and how is the optional onDestroyView called ? Is it called automatically by the system when onDestroy() is triggerred ? In What sequence would this be getting called, when we have an onDestroyView already overridden in our fragment?

It is called when onDestroyView is triggered (using onDestroy would be a memory leak). It is not optional btw.

It is called just before your own (Fragment's) onDestroyView, so you cannot access the binding anymore in your custom onDestroyView.

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