Home > Blockchain >  Why parent of fragment is null after pause?
Why parent of fragment is null after pause?

Time:07-26

When I run app and use it no crash. BottomSheetBehavior works correctly. But if I return to app after a long time pause in background, it crashing with null point exception when try to cast view.parent as View in order to find View with BottomSheetBehavior.

Why fragment don't have parent after pause? How to fix it?

I tried to do binding.root.parent as View and other requireActivity().findViewById(R.id.fragment_sheet_container). But the crash with same case continuous.

@AndroidEntryPoint
class ToolboxSheetFragment : Fragment(R.layout.fragment_toolbox_sheet) {
    private lateinit var binding: FragmentToolboxSheetBinding
    private lateinit var behavior: BottomSheetBehavior<View>

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding = FragmentToolboxSheetBinding.bind(view)
        behavior = BottomSheetBehavior.from(
            view.parent as View // null cannot be cast to non-null type android.view.View
        )
    }

The parent of fragment is main activity with the next layout:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.MainActivity">

    // ...

    <androidx.fragment.app.FragmentContainerView
        android:id="@ id/fragment_sheet_container"
        android:name=".view.fragment.ToolboxSheetFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:behavior_hideable="false"
        app:behavior_peekHeight="?actionBarSize"
        app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
        tools:layout="@layout/fragment_toolbox_sheet" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>
java.lang.RuntimeException: Unable to start activity ComponentInfo{
.view.MainActivity}: java.lang.NullPointerException: null cannot be cast to non-null type android.view.View
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2792)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2870)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1601)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:172)
        at android.app.ActivityThread.main(ActivityThread.java:6590)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
     Caused by: java.lang.NullPointerException: null cannot be cast to non-null type android.view.View
        at .view.fragment.ToolboxSheetFragment.onViewCreated(ToolboxSheetFragment.kt:46)
        at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3128)
        at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:552)
        at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
        at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424)
        at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968)
        at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2886)
        at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:263)
        at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:351)
        at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:246)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1335)
        at android.app.Activity.performStart(Activity.java:7043)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2755)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2870) 
        at android.app.ActivityThread.-wrap11(Unknown Source:0) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1601) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:172) 
        at android.app.ActivityThread.main(ActivityThread.java:6590) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

CodePudding user response:

It would be better if I could get full project. But I am suggesting couple of ways here:

Way 1:

How about keeping behavior inside activity class. instantiate it onCreate(). And use it from ToolboxSheetFragment like this.

class MainActivity : BaseActivity() {
    var behavior: BottomSheetBehavior? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        behavior = BottomSheetBehavior.from(R.id.sheet_id)
    }
}

Then from ToolboxSheetFragment

class ToolboxSheetFragment : Fragment(R.layout.fragment_toolbox_sheet) {
    private lateinit var binding: FragmentToolboxSheetBinding
    var behavior: BottomSheetBehavior? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding = FragmentToolboxSheetBinding.bind(view)
        behavior = (requireActivity() as MainActivity).behavior
    }

Way 2:

Using addOnAttachStateChangeListener on ToolboxSheetFragment:

@AndroidEntryPoint
class ToolboxSheetFragment : Fragment(R.layout.fragment_toolbox_sheet) {
    private lateinit var binding: FragmentToolboxSheetBinding
    private lateinit var behavior: BottomSheetBehavior<View>

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        view.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
            override fun onViewAttachedToWindow(v: View?) {
                if(v!=null){
                    binding = FragmentToolboxSheetBinding.bind(v)
                    behavior = BottomSheetBehavior.from(
                        v.parent as View 
                    )
                }
            }

            override fun onViewDetachedFromWindow(v: View?) {
                view.removeOnAttachStateChangeListener(this)
            }

        })
    }

CodePudding user response:

First inflate the layout in onCreateView() and you can do all your view related work in onViewCreated().

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    binding = FragmentToolboxSheetBinding.inflate(layoutInflater)
    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    behavior = BottomSheetBehavior.from(view)
}
  • Related