Skip to content

Instantly share code, notes, and snippets.

@kikogassen
Created September 26, 2022 13:43
Show Gist options
  • Save kikogassen/4061cc3578f4b38d4b2788ac0d91bac4 to your computer and use it in GitHub Desktop.
Save kikogassen/4061cc3578f4b38d4b2788ac0d91bac4 to your computer and use it in GitHub Desktop.
viewBinding delegate
import android.os.Bundle
import android.view.View
class CustomFragment : Fragment(R.layout.fragment_round_up_accounts) {
private val binding by viewBinding(FragmentCustomBinding::bind)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Use binding here
}
}
import android.os.Handler
import android.os.Looper
import android.view.View
import androidx.fragment.app.Fragment
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.viewbinding.ViewBinding
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
private class FragmentViewBindingDelegate<T : ViewBinding>(
val fragment: Fragment,
val factory: (View) -> T
) : ReadOnlyProperty<Fragment, T>, DefaultLifecycleObserver {
private var binding: T? = null
private var waitingToDestroyBinding = false
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
alertIfNotOnMainThread()
if (waitingToDestroyBinding &&
fragment.viewLifecycleOwner.lifecycle.currentState != Lifecycle.State.DESTROYED
) {
// Don't force clear binding if we're in the DESTROYED state
// so that binding can still be reused within Fragment.onDestroyView
binding = null
waitingToDestroyBinding = false
}
return binding ?: fragment.viewLifecycleOwner.lifecycle.run {
if (!currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
error("Called before onCreateView or after onDestroyView.")
}
factory(thisRef.requireView()).also {
addObserver(this@FragmentViewBindingDelegate)
binding = it
}
}
}
override fun onDestroy(owner: LifecycleOwner) {
// Fragment.viewLifecycleOwner calls FullLifecycleObserver.onDestroy before
// Fragment.onDestroyView, so postpone clearing the value
alertIfNotOnMainThread()
waitingToDestroyBinding = true
Handler(Looper.getMainLooper()).post {
if (waitingToDestroyBinding) {
binding = null
waitingToDestroyBinding = false
}
}
}
private fun alertIfNotOnMainThread() {
if (BuildConfig.DEBUG) {
require(Looper.getMainLooper().isCurrentThread) { "Why aren't you on the main thread?" }
}
}
}
fun <T : ViewBinding> Fragment.viewBinding(factory: (View) -> T): ReadOnlyProperty<Fragment, T> =
FragmentViewBindingDelegate(this, factory)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment