如何将 android 导航 backstack 弹出到某个片段?

Posted

技术标签:

【中文标题】如何将 android 导航 backstack 弹出到某个片段?【英文标题】:How do I pop android navigation backstack up to a certain fragment? 【发布时间】:2021-05-16 05:30:31 【问题描述】:

我再次请求您的智力支持。

我有一个片段,它把自己的多个副本放在导航堆栈上,如下所示:

Navigation.findNavController(button).navigate(
    TaskCollectFragmentDirections.actionTaskCollectFragmentSelf(args.taskIndex, args.screenIndex + 1)
)

上面的代码按预期工作。

问题来了,当我想从导航堆栈中弹出指定数量的这些片段时。 (由按下按钮触发。)

我试过这样做:

Navigation.findNavController(button).popBackStack(lastRecurringFragmentId, false)

我的代码编译并运行,但什么也不做。当我没有给popBackStack 函数提供任何参数时,它会正确地弹出最上面的片段。我还可以使用后退箭头一直导航到起始片段。

如果它很重要,lastRecurringFragmentId 来自这个函数:

fun getLastRecurringFragmentId(recurringFragmentCount: Int): Int 
    val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)!!
    val backStackEntryCount = navHostFragment.childFragmentManager.backStackEntryCount
    val indexOfRequested = backStackEntryCount - recurringFragmentCount
    val requestedEntry = navHostFragment.childFragmentManager.getBackStackEntryAt(indexOfRequested)
    return requestedEntry.id

导航图如下。应该有几个 TaskCollectFragments 和最后一个 TaskEndFragment 在 backstack 的顶部。当用户按下TaskEndFragment 上的按钮时,我想将一些TaskCollectFragments 从堆栈中弹出:

<navigation 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:id="@+id/mobile_navigation"
    app:startDestination="@id/nav_home">

    <fragment
        android:id="@+id/nav_home"
        android:name="ui.home.HomeFragment"
        android:label="@string/menu_home"
        tools:layout="@layout/fragment_home" >
        <action
            android:id="@+id/action_nav_home_to_taskCollectFragment"
            app:destination="@id/taskCollectFragment" />
    </fragment>

    <fragment
        android:id="@+id/taskCollectFragment"
        android:label="TaskCollectFragment"
        tools:layout="@layout/task_collect_fragment"
        >
        <action
            android:id="@+id/action_taskCollectFragment_to_taskEndFragment"
            app:destination="@id/taskEndFragment" />
        <action
            android:id="@+id/action_taskCollectFragment_self"
            app:destination="@id/taskCollectFragment" />
        <argument
            android:name="taskIndex"
            app:argType="integer" />
        <argument
            android:name="screenIndex"
            app:argType="integer" />
    </fragment>
    <fragment
        android:id="@+id/taskEndFragment"
        android:name="ui.task.TaskEndFragment"
        android:label="TaskEndFragment"
        tools:layout="@layout/task_end_fragment">
        <argument
            android:name="taskIndex"
            app:argType="integer" />
    </fragment>
</navigation>

有没有办法可以在满足特定条件之前从后台弹出片段?

【问题讨论】:

您的 getLastRecurringFragmentId() 在使用导航时没有任何意义 - 您传递给 popBackStack() 的 ID 是导航图中的 android:ids。你能包括你的导航图吗? @ianhanniballake 这是否意味着我无法区分片段的多个实例? 如果你说在pop之后你希望你的堆栈是什么会更好?您是否希望弹出所有 taskCollectFragment 实例,而只有 nav_home 仍在堆栈中? @ianhanniballake 正如我所说,我想弹出一些taskCollectFragments。可能只是少数,也可能是全部,视情况而定。感谢您让我知道 ID 的来源,它给了我一个想法! 【参考方案1】:

我创建了两个几乎相同的片段,forSessionTaskCollectFragmentrecurringTaskCollectFragment 具有相同的 viewModel。它们看起来是一样的,从用户的角度来看它们也是一样的。 forSessionTaskCollectFragments 是我想要保留的,recurringTaskCollectFragments 是我想稍后弹出的。

从主页片段,我的导航图转到forSessionTaskCollectFragment,从那里它循环回到自己或转到recurringTaskCollectFragment,它也循环回到自己或最终转到TaskEndFragment

<fragment
    android:id="@+id/nav_home"
    android:name="ui.home.HomeFragment"
    android:label="@string/menu_home"
    tools:layout="@layout/fragment_home" >
    <action
        android:id="@+id/action_nav_home_to_orSessionTaskCollectFragment"
        app:destination="@id/forSessionTaskCollectFragment" />
</fragment>

<fragment
    android:id="@+id/forSessionTaskCollectFragment"
    android:name="ui.task.ForSessionTaskCollectFragment"
    android:label="ForSessionTaskCollectFragment" >
    <action
        android:id="@+id/action_forSessionTaskCollectFragment_self"
        app:destination="@id/forSessionTaskCollectFragment" />
    <action
        android:id="@+id/action_forSessionTaskCollectFragment_to_recurringTaskCollectFragment"
        app:destination="@id/recurringTaskCollectFragment" />
    <argument
        android:name="taskIndex"
        app:argType="integer" />
    <argument
        android:name="screenIndex"
        app:argType="integer" />
</fragment>

<fragment
    android:id="@+id/recurringTaskCollectFragment"
    android:name="ui.task.RecurringTaskCollectFragment"
    android:label="TaskCollectFragment"
    tools:layout="@layout/recurring_task_collect_fragment"
    >
    <action
        android:id="@+id/action_recurringTaskCollectFragment_to_taskEndFragment"
        app:destination="@id/taskEndFragment" />
    <action
        android:id="@+id/action_recurringTaskCollectFragment_self"
        app:destination="@id/recurringTaskCollectFragment" />
    <argument
        android:name="taskIndex"
        app:argType="integer" />
    <argument
        android:name="screenIndex"
        app:argType="integer" />
</fragment>

我将forSessionTaskCollectFragments 放在堆栈上,直到我到达我想要返回的位置。

Navigation.findNavController(button).navigate(
    if(args.screenIndex < viewModel.forSessionScreenCount) 
        ForSessionTaskCollectFragmentDirections.actionForSessionTaskCollectFragmentSelf(args.taskIndex, args.screenIndex + 1)
     else 
        ForSessionTaskCollectFragmentDirections.actionForSessionTaskCollectFragmentToRecurringTaskCollectFragment(args.taskIndex, args.screenIndex + 1)
    
)

之后我把后面要弹回来的片段放回去,最后到TaskEndFragment,从那里我要弹我需要的片段:

Navigation.findNavController(button).navigate(
    if(args.screenIndex < viewModel.screenCount - 2) 
        RecurringTaskCollectFragmentDirections.actionRecurringTaskCollectFragmentSelf(args.taskIndex, args.screenIndex + 1)
     else 
        RecurringTaskCollectFragmentDirections.actionRecurringTaskCollectFragmentToTaskEndFragment(args.taskIndex)
    
)

因此,在TaskEndFragment 的按钮按下时,我可以弹回后台堆栈上我标记为从“会话”片段更改为“重复”片段的确切位置:

Navigation.findNavController(button).popBackStack(R.id.forSessionTaskCollectFragment, false)

这很麻烦,我没有找到任何我想做的事情的例子,但最终设法想出了一个可行的解决方案。 Ianhanniballake 再次感谢,你激发了我的想法。

【讨论】:

你好哦/。这不是你问的问题的答案。您实际上不是回到某个片段,而是向前导航。 有什么解释吗?它按预期工作。我建议你仔细阅读全文。我在添加片段时向前导航。诀窍是在我想要返回的地方更改我添加的框架类型。然后,当我需要后退时,我可以通过调用“Navigation.findNavController(button).popBackStack(R.id.forSessionTaskCollectFragment, false)”来实现,我很确定我不会通过这样做向前导航。

以上是关于如何将 android 导航 backstack 弹出到某个片段?的主要内容,如果未能解决你的问题,请参考以下文章

当backstack为空时,按下android上的后退按钮会破坏导航逻辑

android-clearing backstack 生成 NullPointerException

使用单独的 backstack 创建类似底部导航的 Instagram

导航抽屉backstack,如何让actionbar标题在点击后随片段改变

如何导航回 Nativescript-vue 中的特定组件或 backstack 条目?

使用Android导航组件时如何从后台获取片段?