【问题标题】:Navigation popUpTo and PopUpToInclusive aren't clearing the backstack导航 popUpTo 和 PopUpToInclusive 没有清除后台堆栈
【发布时间】:2019-09-19 19:23:55
【问题描述】:

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

Login screenEmail Login screen

我想要的是,在导航到电子邮件登录屏幕后,当我按下返回时,应用程序退出。这意味着登录屏幕的后台堆栈被删除。我知道登录屏幕不应该以这种方式工作,但我仍然只是想办法解决问题。

我遵循了来自 Google 的 Get started with the Navigation component 的文档。它说,使用app:popUpToapp:popUpToInclusive="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" />

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
}

我给一个按钮一个点击事件。在其中,我使用导航控制器通过为其提供操作 ID 来导航到电子邮件登录屏幕。在&lt;action&gt;中,有app:popUpToapp:popUpToInclusive="true"

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

【问题讨论】:

  • 记录在案。 Documentation says 可以像使用登录片段一样使用登录片段。我不明白为什么“登录屏幕不应该那样工作”
  • @PanosGr 你指的是 “例如,如果你的应用有一个初始登录流程,一旦用户登录,你应该从后退堆栈,以便“后退”按钮不会将用户带回登录流程”?好吧,就我而言,用户尚未登录。它只是打开一个不同的登录方法。通常,应允许用户返回主登录屏幕并选择不同的登录方式。

标签: android kotlin navigation android-jetpack fragment-backstack


【解决方案1】:
<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"/>

您的 popUpTo 将返回电子邮件登录,然后因为包含而弹出它。 如果您将 popUpTo 更改为您的登录片段,由于包含标志,它将被导航回并弹出,这将导致您想要的行为。

【讨论】:

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

这两行让这个技巧奏效:

如果你想从 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>

如果你把日志放到你的片段中,你可以看到片段A在使用这个动作调用片段B之后被销毁了。

【讨论】:

  • 谢谢,感谢您的帮助。但是里托已经给出了答案,我想你的答案基本是一样的。
  • 请记住,如果你有一个片段链 A -> B -> C -> D 那么你需要在 popUpTo 操作参数中传递以下内容,以退出 backPress 行为。 app:popUpTo="@id/A", app:popUpTo="@id/B", app:popUpTo="@id/C"
  • 感谢您的帮助!
【解决方案3】:

您可以像 this answer does 一样在 XML 中执行此操作,也可以通过编程方式执行此操作:

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

【讨论】:

  • 我认为这与接受的答案相同,但以编程方式完成。不过不错。我可能有一天会需要它。
  • 你能帮我解决这个问题吗:stackoverflow.com/questions/64687574/…,谢谢。
【解决方案4】:

我为尚未完全理解的人写这个答案 方式popUpTo 有效,我希望它的示例对某人有所帮助,因为大多数 导航示例在大多数网站中都是重复的,并且不显示 整个画面。

在任何&lt;action&gt;中,如果我们为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"/>
  1. 对于 F->G,我们写:
<action
    ...
    app:destination="@+id/G"
    app:popUpTo="@+id/B"
    app:popUpToInclusive="true"/>

然后在使用E->F动作从E到F之后,最后一个片段(F)和C之间的片段(在E-&gt;FpopUpTo中定义)将被移除。由于app:popUpToInclusive="false",这次不会移除分片C,所以我们的back stack变成:

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

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

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

【讨论】:

  • 伟大的 LIFO 解释 - 谢谢!
【解决方案5】:

假设您的应用具有三个目的地——A、B 和 C——以及从 A 到 B、B 到 C 和 C 返回到 A 的操作。相应的导航图如图所示

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

<fragment
android:id="@+id/c"
android:name="com.example.myapplication.C"
android:label="fragment_c"
tools:layout="@layout/fragment_c">

<action
    android:id="@+id/action_c_to_a"
    app:destination="@id/a"
    app:popUpTo="@+id/a"
    app:popUpToInclusive="true"/>

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

【讨论】:

【解决方案6】:

popUpTo它定义了当你按下返回时你想去的地方。如果你设置popUpInclusive = true,导航也会跳过那个地方(popUpTo)。

【讨论】:

  • 我认为这是迄今为止最好的解释
【解决方案7】:

示例:A -> B -> A

FragmentB.kt

尝试弹出控制器的回栈

private fun popBackStackToA() {
    if (!findNavController().popBackStack()) {
//            Call finish on your Activity
        requireActivity().finish()
    }
}

Back Stack

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-29
    • 1970-01-01
    • 2011-08-13
    • 1970-01-01
    • 2015-04-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多