导航时导航主机变为空(Android导航组件) [英] Navigation Host becomes null while navigating (Android Navigation Component)

查看:99
本文介绍了导航时导航主机变为空(Android导航组件)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个游戏,用户在其中浏览5个屏幕.在最后一个屏幕上,用户可以选择结束游戏,然后将他们带回到开始屏幕.我的问题出在用户结束游戏然后再次开始时.在应用程序中导航时,找不到导航主机片段.

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.

我尝试使用不同的视图查找导航主机,并且在调试时,我发现对于找不到它的片段,其父级等于null.

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.

这是我浏览的地方,位于片段onViewCreated()

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)
            }

保存导航主机片段的MainActivity的布局为:

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.

以下是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>

这是崩溃后给出的消息: java.lang.IllegalStateException: View android.widget.ScrollView{637e4ce VFED.V... ......ID 0,0-1440,2308} does not have a NavController set

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

推荐答案

LiveData会记住当前数据,并在观察者再次启动时自动将其重新传送,从而使其不适用于触发导航操作的事件:您对<每次启动Fragment时都会触发c6>,因此无法真正弹出该Fragment.

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.

请注意,在上的堆栈中,碎片不会被销毁.如果要在Fragment放在堆栈上时更改Fragment所依赖的基础数据,则在观察时,对于传递给observe()的LifecycleOwner,应使用viewLifecycleOwner而不是this(代表Fragment). onViewCreated().这样可以确保一旦视图被破坏(即,进入后退堆栈),您将不再获得观察者回调.

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!!在Fragment中用作LifecycleOwner绝对总是错误的,因为这意味着即使Fragment被完全销毁,观察者也不会被清理(只会被清理掉).活动被破坏时清除).

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).

根据条件导航文档,建议的方法是确保您的LiveData跟踪的是状态而不是事件.这样,在调用navigate()之后,您可以更新状态以确保第二次发生回调时,您不会再次调用navigate().建议在上使用此方法SingleLiveEvent方法.

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.

这篇关于导航时导航主机变为空(Android导航组件)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆