Navigation Host becomes null while navigating (Android Navigation Component)

懵懂的女人 提交于 2019-12-11 05:43:08

问题


I am creating a game where the user goes through a series of 5 screens. At the last screen, the user has the choice to end the game, at which point they are taken back to the starting screen. My problems come in when a user ends the game and then starts again. While navigating through the app, the navigation host fragment cannot be found.

The first time through the app, it navigates at usual, but the second time, the navigation host cannot be found.

I have tried using different views to find the navigation host, and while debugging, I saw that for the fragment where it can not be found, the parent is equal to null.

This is where I navigate, in the fragments onViewCreated()

    viewModel.getGameUpdates().observe(activity!!, Observer { updatedGame ->
                if(updatedGame.playerList.size == 0){
                    Log.d("END","END")
                    viewModel.endGame()
                }
                adapter?.players = updatedGame.playerList

                if(updatedGame.started){
                    Navigation.findNavController(view).navigate(R.id.action_waitingFragment_to_gameFragment)
                }
            })

and this is the moment where the user clicks to navigate back to the first screen:

     btn_end_game.setOnClickListener {
                viewModel.endGame()
                timer.cancel()
                Navigation.findNavController(view).navigate(R.id.action_gameFragment_to_startFragment)
            }

The layout for my MainActivity that holds the navigation host fragment is:

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


        <fragment
                android:id="@+id/nav_host_fragment"
                android:name="androidx.navigation.fragment.NavHostFragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:defaultNavHost="true"
                app:navGraph="@navigation/nav_graph" />

    </FrameLayout>

I do realize that I am just adding on top of the back stack when I would rather pop back to the first fragment. I am just lost as to how the fragment is null.

The following is the nav_graph.xml

    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:app="http://schemas.android.com/apk/res-auto"
                android:id="@+id/nav_graph" app:startDestination="@id/startFragment">

        <fragment android:id="@+id/startFragment" android:name="com.dangerfield.spyfall.start.StartFragment"
                  android:label="StartFragment">
            <action android:id="@+id/action_startFragment_to_joinGameFragment" app:destination="@id/joinGameFragment"/>
            <action android:id="@+id/action_startFragment_to_newGameFragment" app:destination="@id/newGameFragment"/>
        </fragment>
        <fragment android:id="@+id/newGameFragment" android:name="com.dangerfield.spyfall.newGame.NewGameFragment"
                  android:label="NewGameFragment">
            <action android:id="@+id/action_newGameFragment_to_waitingFragment" app:destination="@id/waitingFragment"/>
        </fragment>
        <fragment android:id="@+id/joinGameFragment" android:name="com.dangerfield.spyfall.joinGame.JoinGameFragment"
                  android:label="JoinGameFragment">
            <action android:id="@+id/action_joinGameFragment_to_waitingFragment" app:destination="@id/waitingFragment"/>
        </fragment>
        <fragment android:id="@+id/waitingFragment" android:name="com.dangerfield.spyfall.waiting.WaitingFragment"
                  android:label="WaitingFragment">
            <action android:id="@+id/action_waitingFragment_to_gameFragment" app:destination="@id/gameFragment"/>
            <action android:id="@+id/action_waitingFragment_to_startFragment" app:destination="@id/startFragment"/>
        </fragment>
        <fragment android:id="@+id/gameFragment" android:name="com.dangerfield.spyfall.game.GameFragment"
                  android:label="GameFragment">
            <action android:id="@+id/action_gameFragment_to_startFragment" app:destination="@id/startFragment"/>
        </fragment>
    </navigation>

This is the message given after crash: java.lang.IllegalStateException: View android.widget.ScrollView{637e4ce VFED.V... ......ID 0,0-1440,2308} does not have a NavController set


回答1:


LiveData remembers the current data and will automatically redeliver it when the observer becomes started again, making it inappropriate for events that trigger navigation operations: your operation to navigate() is going to be triggered every time your Fragment is started, making it impossible to actually pop back to that Fragment.

Note that Fragments are not destroyed while on the back stack. If you're changing the underlying data that your Fragment relies on while that Fragment is on the back stack, you should use the viewLifecycleOwner instead of this (representing the Fragment) for your LifecycleOwner passed to observe() when observing in onViewCreated(). This ensures that you will no longer get observer callbacks once your view is destroyed (i.e., you go onto the back stack).

activity!! is absolutely always wrong to use as the LifecycleOwner from within a Fragment, since that means the observer will not be cleaned up even if the Fragment is completely destroyed (it'll only be cleaned up when the activity is destroyed).

As per the conditional navigation documentation, the recommended approach is to ensure that your LiveData is tracking state rather than events. That way, after you call navigate(), you can update the state to ensure that when the callback happens a second time, you don't call navigate() a second time. This approach is recommended over the SingleLiveEvent approach.



来源:https://stackoverflow.com/questions/56602954/navigation-host-becomes-null-while-navigating-android-navigation-component

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!