-
-
Save Zhuinden/ea3189198938bd16c03db628e084a4fa to your computer and use it in GitHub Desktop.
// 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) |
Fatal Exception: java.lang.IllegalStateException: Can't access the Fragment View's LifecycleOwner when getView() is null i.e., before onCreateView() or after onDestroyView() at androidx.fragment.app.Fragment.getViewLifecycleOwner(Fragment.java:361) at net.example.company.utils.views.FragmentViewBindingDelegate.getValue(FragmentViewBindingDelegate.java:61) at net.example.company.views.smartdogtrainer.common.FragBlah.getBinding(FragBlah.java:80) at net.example.company.views.smartdogtrainer.common.FragBlah.visibilityConnectingContainer(FragBlah.java:295) at net.example.company.views.smartdogtrainer.common.FragBlah.resetVisibility(FragBlah.java:289) at net.example.company.views.smartdogtrainer.common.FragBlah.showErrorConnection(FragBlah.java:311) at net.example.company.views.smartdogtrainer.common.FragBlah.performErrorConnection$lambda-8$lambda-7(FragBlah.java:332) at net.example.company.views.smartdogtrainer.common.FragBlah$$InternalSyntheticLambda$0$deef3459a1d7d120afdfd9de73e823d74825ffa0a9101b2fc6484805f55a8632$0.run$bridge(FragBlah.java:13) at android.app.Activity.runOnUiThread(Activity.java:7154) at net.example.company.views.smartdogtrainer.common.FragBlah.performErrorConnection(FragBlah.java:331) at net.example.company.views.smartdogtrainer.common.FragBlah.access$performErrorConnection(FragBlah.java:63) at net.example.company.views.smartdogtrainer.common.FragBlah$sdtCollarScannerCallback$1.onStopScanning(FragBlah.java:120) at net.example.company.scanner.BleScanner.stopScanningForDevices(BleScanner.java:128) at net.example.company.scanner.BleScanner.durationTimeoutScanning(BleScanner.java:116) at net.example.company.scanner.BleScanner.access$durationTimeoutScanning(BleScanner.java:15) at net.example.company.scanner.BleScanner$durationScanningRunnable$1.run(BleScanner.java:52) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:246) at android.app.ActivityThread.main(ActivityThread.java:8595) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)And my delegate:
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).not()) { 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) }Have you found the cause of this error? I also encountered this error when implementing it in a fragment. My code below: override val binding by viewBinding { rcData.adapter = null }
Stop trying to update views after the views are destroyed, check with
if(isAdded()) {}
or cancel your task inonDestroyView
so that this doesn't happen.You can also wrap it in a try-catch if you just want to ignore it.
i call:
binding.rvData.post {
binding.rvData.layoutManager?.scrollToPosition(0)
}
onViewCreated(). However, for some reason, it is not executed immediately but instead runs after onDestroyView is called.
=> crash
Do you have any way to fix it? Thanks
Fatal Exception: java.lang.IllegalStateException: Can't access the Fragment View's LifecycleOwner when getView() is null i.e., before onCreateView() or after onDestroyView() at androidx.fragment.app.Fragment.getViewLifecycleOwner(Fragment.java:361) at net.example.company.utils.views.FragmentViewBindingDelegate.getValue(FragmentViewBindingDelegate.java:61) at net.example.company.views.smartdogtrainer.common.FragBlah.getBinding(FragBlah.java:80) at net.example.company.views.smartdogtrainer.common.FragBlah.visibilityConnectingContainer(FragBlah.java:295) at net.example.company.views.smartdogtrainer.common.FragBlah.resetVisibility(FragBlah.java:289) at net.example.company.views.smartdogtrainer.common.FragBlah.showErrorConnection(FragBlah.java:311) at net.example.company.views.smartdogtrainer.common.FragBlah.performErrorConnection$lambda-8$lambda-7(FragBlah.java:332) at net.example.company.views.smartdogtrainer.common.FragBlah$$InternalSyntheticLambda$0$deef3459a1d7d120afdfd9de73e823d74825ffa0a9101b2fc6484805f55a8632$0.run$bridge(FragBlah.java:13) at android.app.Activity.runOnUiThread(Activity.java:7154) at net.example.company.views.smartdogtrainer.common.FragBlah.performErrorConnection(FragBlah.java:331) at net.example.company.views.smartdogtrainer.common.FragBlah.access$performErrorConnection(FragBlah.java:63) at net.example.company.views.smartdogtrainer.common.FragBlah$sdtCollarScannerCallback$1.onStopScanning(FragBlah.java:120) at net.example.company.scanner.BleScanner.stopScanningForDevices(BleScanner.java:128) at net.example.company.scanner.BleScanner.durationTimeoutScanning(BleScanner.java:116) at net.example.company.scanner.BleScanner.access$durationTimeoutScanning(BleScanner.java:15) at net.example.company.scanner.BleScanner$durationScanningRunnable$1.run(BleScanner.java:52) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:246) at android.app.ActivityThread.main(ActivityThread.java:8595) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)And my delegate:
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).not()) { 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) }Have you found the cause of this error? I also encountered this error when implementing it in a fragment. My code below: override val binding by viewBinding { rcData.adapter = null }
Stop trying to update views after the views are destroyed, check with
if(isAdded()) {}
or cancel your task inonDestroyView
so that this doesn't happen.
You can also wrap it in a try-catch if you just want to ignore it.i call: binding.rvData.post { binding.rvData.layoutManager?.scrollToPosition(0) } onViewCreated(). However, for some reason, it is not executed immediately but instead runs after onDestroyView is called. => crash Do you have any way to fix it? Thanks
You're using handler.post {}
, so it's possible for it to run after onDestroyView
.
Stop trying to update views after the views are destroyed, check with
if(isAdded()) {}
or cancel your task inonDestroyView
so that this doesn't happen.You can also wrap it in a try-catch if you just want to ignore it.