fragmentviewbindingdelegate-kt
fragmentviewbindingdelegate-kt copied to clipboard
Add ViewBinding::inflate support with `viewInflateBinding`
We can switch the factory to work with fragment to add support for ViewBinding::inflate as an alternative (which should resolve #8). I also switched to inline functions to avoid multiple lambdas/callbacks
Technically this would be a breaking change, since the constructor is public and the signature changes, even though nobody should be affected 🤔
@bleeding182 I've used your code like:
private val binding by viewInflateBinding(FragProductsBinding::inflate)
but, without writing:
class FragProduct : Fragment()
...
override fun onCreateView(
inflater: LayoutInflator,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = binding.root
It doesn't show the view on the screen; It's white.
This is my delegate class:
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.app.AppCompatActivity
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: (Fragment) -> T,
val cleanUp: ((T?) -> Unit)?
) : ReadOnlyProperty<Fragment, T> {
// A backing property to hold our value
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) {
cleanUp?.invoke(binding)
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).also { this.binding = it }
}
}
inline fun <T : ViewBinding> Fragment.viewBinding(
crossinline viewBindingFactory: (View) -> T,
noinline cleanUp: ((T?) -> Unit)? = null
): FragmentViewBindingDelegate<T> =
FragmentViewBindingDelegate(this, { f -> viewBindingFactory(f.requireView()) }, cleanUp)
inline fun <T : ViewBinding> Fragment.viewInflateBinding(
crossinline bindingInflater: (LayoutInflater) -> T,
noinline cleanUp: ((T?) -> Unit)? = null,
): FragmentViewBindingDelegate<T> =
FragmentViewBindingDelegate(this, { f -> bindingInflater(f.layoutInflater) }, cleanUp)
inline fun <T : ViewBinding> AppCompatActivity.viewInflateBinding(
crossinline bindingInflater: (LayoutInflater) -> T
) =
lazy(LazyThreadSafetyMode.NONE) {
bindingInflater.invoke(layoutInflater)
}
Also, I found this: https://github.com/Zhuinden/fragmentviewbindingdelegate-kt/pull/7#issuecomment-796466177