androidbroadcast / ViewBindingPropertyDelegate

Make work with Android View Binding simpler
https://proandroiddev.com/make-android-view-binding-great-with-kotlin-b71dd9c87719
Apache License 2.0
1.42k stars 102 forks source link

IllegalStateException when using viewBinding for onCreateView #101

Open gabrielfeo opened 2 years ago

gabrielfeo commented 2 years ago

Trying to update to 1.5.2+, I find this usage causes a crash due to a new internal check in the library.

    private val binding by viewBinding {
        OnboardingContainerFragmentBinding.inflate(layoutInflater)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View = binding.root
IllegalStateException: Fragment’s view can’t be accessed. Fragment’s view is null.
Maybe you try to access view before onViewCreated() or after onDestroyView().
Add check `if (view != null)` before call ViewBinding

The change is stated in the changelog, but I don't see anything inherently wrong with the above usage considering the viewBinding lambda is invoked lazily. Do you see a way to keep it (we have many in the project) when upgrading to 1.5.2+?

kirich1409 commented 2 years ago

Please try version 1.5.6

ddello32 commented 1 year ago

I've have been facing the same issue with version 1.5.6

kirich1409 commented 1 year ago

@gabrielfeo from what places you access viewBinding delegate?

cmorigaki commented 1 year ago

@kirich1409 I could replicate the problem using the sample app adjusting the way we setup the Fragment view. Instead of referecing the xml, using the Binding generated class

from:

class ProfileFragment : Fragment(R.layout.fragment_profile) {

    private val viewBinding by viewBinding(FragmentProfileBinding::bind,
        onViewDestroyed = { _: FragmentProfileBinding ->
            // reset view
        })

to:

class ProfileFragment : Fragment() {

    private val viewBinding by viewBinding(FragmentProfileBinding::bind,
        onViewDestroyed = { _: FragmentProfileBinding ->
            // reset view
        })

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ) = viewBinding.root
rpavliuk commented 1 year ago

Is it possible to fix this without removing the XML referencing?

rpavliuk commented 1 year ago

@gabrielfeo from what places you access viewBinding delegate?

@kirich1409 In my case, I'm accessing the viewBinding delegate in the onViewCreated method. This should be ok. Is it?

kirich1409 commented 1 year ago

Yes. onCreateView() only need to inflate view from XML or create in code. Only after the callback method Fragment.getView() will return View connected with Fragment. That why all view initialization must be done in onViewCreated independently are you using ViewBinding or not.

kirich1409 commented 1 year ago

Another variant is

class ProfileFragment : Fragment() {
    private val viewBinding: ProfileBinding by viewBinding(createMethod = CreateMethod.INFLATE)

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ) = viewBinding.root
}

it will work because ViewBindingPropdetyDelegate will call ViewBinding.inflate method via reflection. It's slower and not recommended.

Prefer to use

class ProfileFragment : Fragment(R.layout.profile) {
    private val viewBinding: ProfileBinding by viewBinding(ProfileBinding::bind)
}
kirich1409 commented 1 year ago

By default the library prevent using viewBinding delegate before onViewCreated() will return the result. That's why crash happens. It's expected behaviour. I will think about @gabrielfeo case will work without turning off the check

mahmud0v commented 4 months ago

@kirich1409 Crash happened on android 13, 14 version which is using viewBinding delegate 1.5.9 version . I used this type

class ProfileFragment : Fragment(R.layout.profile) {
    private val viewBinding: ProfileBinding by viewBinding(ProfileBinding::bind)
}

Exception java.lang.IllegalStateException: Fragment's view can't be accessed. Fragment isn't added at by.kirich1409.viewbindingdelegate.LifecycleViewBindingProperty.getValue (ViewBindingProperty.kt:87) at by.kirich1409.viewbindingdelegate.FragmentViewBindingProperty.getValue (FragmentViewBindings.kt:63) at by.kirich1409.viewbindingdelegate.FragmentViewBindingProperty.getValue (FragmentViewBindings.kt:52)

I replaced this code part it worked:

class ProfileFragment : Fragment(R.layout.profile) {
    private var _binding: ProfileBinding? = null
    private val binding get() = _binding!!

   override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View? {
        _binding = ProfileBinding.inflate(inflater, container, false)
        return binding.root
    }
}
fearsom03 commented 3 months ago

any updates on this issue ?

mahmud0v commented 3 months ago

any updates on this issue ? I don't have any info about this update. Do you have the same issue when using viewBinding delegate with 13, 14 android version

kirich1409 commented 3 months ago

I understand the issue. Try to reproduce it and fix on weekend

kirich1409 commented 3 months ago

As solution I recommend to set XML layout in Fragment constructor and use ViewBindingDelegate without without inflating view and it will wrap Fragment.getView()