导航popUpTo和PopUpToInclusive不会清除后退堆栈

adr*_*ilz 5 navigation android kotlin fragment-backstack android-jetpack

我是Android Jetpack导航架构的新手。我正在一个新的应用程序上尝试。有一个活动和一些片段,其中两个是登录屏幕和电子邮件登录屏幕。我在导航XML中定义了这些片段。该应用程序的流程如下:

Login screenEmail Login screen

我想要的是,导航到电子邮件登录屏幕后,当我按返回时,该应用程序退出。表示已删除登录屏幕的后堆栈。我知道登录屏幕不应该这样工作,但我仍然只是想办法解决。

我遵循了Google 导航组件入门中的文档。它说,使用app:popUpToapppopUpToInclusive="true"应该清除后台堆栈,但是当我在电子邮件登录屏幕上按回去时,它仍然返回登录而不是退出。

所以,这就是我尝试过的。

nav_main.xml

<fragment android:id="@+id/loginFragment"
          android:name="com.example.myapp.ui.main.LoginFragment"
          android:label="@string/login"
          tools:layout="@layout/fragment_login" >

    <action
        android:id="@+id/action_login_to_emailLoginFragment"
        app:destination="@id/emailLoginFragment"
        app:popEnterAnim="@anim/slide_in_right"
        app:popExitAnim="@anim/slide_out_right"
        app:popUpTo="@+id/emailLoginFragment"
        app:popUpToInclusive="true"/>

</fragment>

<fragment android:id="@+id/emailLoginFragment"
          android:name="com.example.myapp.ui.main.EmailLoginFragment"
          android:label="EmailLoginFragment"
          tools:layout="@layout/fragment_login_email" />
Run Code Online (Sandbox Code Playgroud)

LoginFragment.kt

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    binding.emailLoginButton.setOnClickListener {
        findNavController().navigate(R.id.action_login_to_emailLoginFragment)
    }

    return binding.root
}
Run Code Online (Sandbox Code Playgroud)

我给按钮单击事件。在其中,我使用了导航控制器,通过为其提供操作的ID导航到电子邮件登录屏幕。在中<action>,有app:popUpToapp:popUpToInclusive="true"

一遍又一遍地阅读文档,并阅读了许多StackOverflow问题之后,我发现这些属性应该可以将我的登录屏幕从后栈中删除。但是他们没有。该按钮确实导航到电子邮件登录屏幕,但是当我按返回时,它仍然返回登录屏幕,而不是退出应用程序。我想念什么?

Ami*_*ami 55

我为那些还没有完全理解其popUpTo工作方式的人写这个答案,我希望它的示例对某人有所帮助,因为大多数导航示例在大多数网站中都是重复的,并且没有显示整个图片。

在any中,<action>如果我们为 写入一个值app:popUpTo,则意味着我们要在完成操作后立即从返回堆栈中删除一些片段,但是当操作完成时将从返回堆栈中删除哪些片段?

它的顺序是后进先出,因此:

  • 最后一个片段和中定义的片段之间的所有片段都popUpTo将被删除。
  • 如果我们添加app:popUpToInclusive="true",那么 中定义的片段popUpTo也将被删除。

示例: 考虑导航图中从 A 到 G 的片段,如下所示:

A->B->C->D->E->F->G

我们可以从 A 到 B,然后从 B 到 C,依此类推。考虑以下两个操作:

  1. 我们写一个动作E->F :
<action
    ...
    app:destination="@+id/F"
    app:popUpTo="@+id/C"
    app:popUpToInclusive="false"/>
Run Code Online (Sandbox Code Playgroud)
  1. 对于F->G我们写:
<action
    ...
    app:destination="@+id/G"
    app:popUpTo="@+id/B"
    app:popUpToInclusive="true"/>
Run Code Online (Sandbox Code Playgroud)

然后,在使用操作E->FpopUpTo从 E 到 F 后,最后一个片段 (F) 和 C (在of中定义E->F)之间的片段将被删除。这次片段 C 不会被删除,因为app:popUpToInclusive="false"我们的返回堆栈变成:

A->B->C->F(F 目前位于顶部)

现在,如果我们使用操作F-> G 转到片段 G :最后一个片段(G)和 B (在popUpToof中定义F->G)之间的所有片段都将被删除,但这次片段 B 也将被删除,因为在 F->我们写的G动作app:popUpToInclusive="true"。所以返回堆栈变成:

A->G(G 现在在最上面)


小智 24

这两行使技巧起作用:

如果你想从 A 到 B 并期望完成 A:

您需要通过此操作调用 B:

    <fragment
        android:id="@+id/fragmentA"            
        tools:layout="@layout/fragment_a">

        <action
            android:id="@+id/action_call_B"
            app:destination="@+id/fragmentB"
            app:popUpTo="@id/fragmentA"
            app:popUpToInclusive="true" />

    </fragment>

    <fragment
        android:id="@+id/fragmentB"
        tools:layout="@layout/fragment_b">


    </fragment>
Run Code Online (Sandbox Code Playgroud)

如果您将日志放入片段中,您可以看到在使用此操作调用 fragmentB 后 fragmentA 被销毁。


Ven*_*Ren 14

您可以像这个答案一样在 XML 中执行此操作,也可以以编程方式执行此操作:

NavOptions navOptions = new NavOptions.Builder().setPopUpTo(R.id.loginRegister, true).build();
Navigation.findNavController(mBinding.titleLogin).navigate(R.id.login_to_main, null, navOptions);
Run Code Online (Sandbox Code Playgroud)

  • 我认为这与接受的答案相同,但以编程方式完成。不过不错。有一天我可能需要它。 (2认同)

小智 14

popUpTo它定义了当你按回键时你想去的地方。如果您设置popUpInclusive = true,导航也会跳过该位置(中popUpTo)。


小智 10

假设您的应用程序具有三个目的地\xe2\x80\x94A、B 和 C\xe2\x80\x94,以及从 A 到 B、B 到 C 以及 C 返回 A 的操作。相应的导航图形如图所示

\n

具有三个目的地的圆形导航图:A、B 和 C。

\n

对于每个导航操作,都会将一个目的地添加到返回堆栈中。如果您要反复导航此流程,则返回堆栈将包含每个目的地的多组(A、B、C、A、B、C、A 等)。为了避免这种重复,您可以在从目标 C 到目标 A 的操作中指定 app:popUpTo 和 app:popUpToInclusive,如下例所示:

\n
<fragment\nandroid:id="@+id/c"\nandroid:name="com.example.myapplication.C"\nandroid:label="fragment_c"\ntools:layout="@layout/fragment_c">\n\n<action\n    android:id="@+id/action_c_to_a"\n    app:destination="@id/a"\n    app:popUpTo="@+id/a"\n    app:popUpToInclusive="true"/>\n
Run Code Online (Sandbox Code Playgroud)\n\n

到达目的地 C 后,返回堆栈包含每个目的地(A、B、C)的一个实例。当导航回目的地 A 时,我们还会 popUpTo A,这意味着我们在导航时从堆栈中删除 B 和 C。使用 app:popUpToInclusive="true",我们还将第一个 A 从堆栈中弹出,从而有效地清除它。请注意,如果您不使用 app:popUpToInclusive,您的返回堆栈将包含目标 A 的两个实例

\n

  • 请注意,此答案的内容是从官方文档复制的。https://developer.android.com/guide/navigation/navigation-navigate#pop-example (3认同)

Rit*_*ito 7

<action
        android:id="@+id/action_login_to_emailLoginFragment"
        app:destination="@id/emailLoginFragment"
        app:popEnterAnim="@anim/slide_in_right"
        app:popExitAnim="@anim/slide_out_right"
        app:popUpTo="@+id/loginFragment"
        app:popUpToInclusive="true"/>
Run Code Online (Sandbox Code Playgroud)

您的popUpTo将返回到电子邮件登录名,然后由于包含在内而将其弹出。如果将popUpTo更改为您的登录片段,由于包含标记,它将被导航回并弹出,这将导致您期望的行为。

  • @Fugogugo 当您创建操作时,您可以添加 `popUpTo` 值。当您使用此操作导航到目的地并返回导航时,导航会将您带回“popUpTo”片段,在上面的代码示例中,即为“loginFragment”。设置 `popUpToInclusive="true"` 告诉操作“当您返回导航时,请同时删除 `popUpTo` 中的片段”,因此它也会删除 `loginFragment` 。这有帮助吗? (37认同)
  • ....我试图理解这个答案,但我不能。有人可以向我解释得更清楚吗? (9认同)
  • 对我来说有效,但仍然有一个小故障,即在 B 片段上我仍然看到“向上箭头按钮” - 这是意料之外的 (3认同)