如何使用新的导航架构组件禁用某些片段中的“向上”导航?

27
我正在尝试使用新的Navigation Architecture Component,但我无法弄清楚如何做到以下几点:
我有1个Activity(MainActivity)+ 3个Fragment:
- SplashFragment(主页) - MainFragment - SignUpFragment
我想使用SplashFragment来确定是否导航到MainFragment或SignUpFragment,但一旦到达这两个片段中的任何一个,您就不能弹回到SplashFragment。如何使用新的导航组件实现?
我在调用navigate(R.id.action_xxx)之前和之后尝试了popBackStack,但都没有起作用(这是有道理的:在此之前没有任何内容可以弹出;在此之后只会关闭刚添加的片段)。这是否意味着唯一的方法是覆盖onBackPressed以拦截它并确保在这些情况下不会调用navigateUp
谢谢!
6个回答

32

首先,在action标签中添加属性app:popUpTo='your_nav_graph_id'app:popUpToInclusive="true"

<fragment
    android:id="@+id/signInFragment"
    android:name="com.glee.incog2.android.fragment.SignInFragment"
    android:label="fragment_sign_in"
    tools:layout="@layout/fragment_sign_in" >
    <action
        android:id="@+id/action_signInFragment_to_usersFragment"
        app:destination="@id/usersFragment"
        app:launchSingleTop="true"
        app:popUpTo="@+id/main_nav_graph"
        app:popUpToInclusive="true" />
</fragment>

其次,使用上述操作作为参数导航到目的地。

findNavController(fragment).navigate(SignInFragmentDirections.actionSignInFragmentToUserNameFragment())

注意: 如果使用 navigate(@IdRes int resId) 方法导航,将无法获得所需的结果。因此,我使用了 navigate(@NonNull NavDirections directions) 方法。


popUpTo的值应该是根图形的ID,而不是nav_graph_id,因此对我来说,使用navigate(@IdRes int resId)完全没问题。 - theme_an
你能具体说明一下吗? - Cyph3rCod3r
10
为了澄清像我一样困惑的人 - popUpTo 的意思是“删除此片段之前的所有内容”,如果将 popUpToInclusive 设置为 true,则意味着“删除此片段和它之前的所有内容”。另外,请确保在导航时使用操作的 ID,而不是要导航到的片段的 ID。 - bug56
3
请注意:您需要将 Navigation 组件 Safe Args 插件添加到项目中,才能生成第二步中的 *Directions 类。 - soren121
2
它可以使用返回按钮,但是在MainFragment中可见的导航向上按钮,点击向上后会导航到splashFragment。如何解决这个问题? - shantanu
@shantanu 你可以配置一组顶级目的地(即这些片段不会显示向上按钮)。请参考此答案:https://dev59.com/b1UL5IYBdhLWcg3weH4j#53463839 - Xam

8

在alpha05版本中,这对我有用。在action标签中添加app:popUpTo="@id/nav_graph"(在您的nav_graph.xml文件中)。 这里"@id/nav_graph"是我的图形或根的ID。

<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/nav_graph"
app:startDestination="@id/startFragment">
.......
<action
android:id="@+id/action_startFragment_to_homeFragment"
app:destination="@id/homeFragment" 
app:popUpTo="@id/nav_graph"/>
.......

您可以在设计选项卡中完成此操作:选择“SplashFragment”,选择要更改的操作,然后选择“root”作为“弹出到”的选项。

你好,欢迎来到StackOverflow。您能否重新格式化您的答案,以便更容易理解? - Abhinav
我认为最好将popUpTo值设置为图形根节点的id,也就是你的startDestination id值,因为clearTask属性已经被弃用了。你还应该将popUpToInclusive设置为true。 - theme_an

7

警告:clearTask已被废弃,并将在未来的版本中删除,目前还不确定解决方案。请暂时关注此问题以保持最新状态。


哦,终于在10分钟后找到了关键:使用clearTask

我所要做的就是将app:clearTask="true"添加到特定操作中,或者使用.navigate(R.id.actionXXXX, null, NavOptions.Builder().setClearTask(true).build()),然后就完成了。只需确保将其添加到SplashFragment的所有子项中(在这种情况下,包括MainFragment和SignUpFragment)。


1
但它说setClearTask已被弃用。我们不应该寻找替代方案吗? - MuM6oJuM6o
@MuM6oJuM6o 不错的发现!我刚在这个问题上发表了评论(https://issuetracker.google.com/issues/80338878),希望很快能得到回复。我还会在答案中添加一个警告。 - Louis Tsai
1
我在文档中查找了一些信息,结果发现Google有另一种方法来实现这个目标。它被称为NavOptions.setPopUpTo(destinationId:Int , inclusive:boolean)。不幸的是,我无法达到预期的结果。 链接 - MuM6oJuM6o
1
@MuM6oJuM6o 我也遇到了同样的问题,但是仔细查看了clearTask xml属性的弃用消息后,它说“将popUpTo设置为您的图形的根,并使用popUpToInclusive”,我按照它所说的做了,并实现了期望的行为。通过这种方式,您仍然可以使用findNavController().navigate(@IdRes resId, bundle)。 - theme_an

3

如果您有闪屏碎片和主碎片,并且您不希望在主碎片之后返回到闪屏碎片,使用下面的方法可以实现此目的。

<fragment
     android:id="@+id/splashFragment"
     android:name="com.example.youappname.views.SplashFragment"
     android:label="fragment_splash"
     tools:layout="@layout/fragment_splash">
     <action
         android:id="@+id/action_splashFragment_to_mainFragment"
         app:destination="@id/mainFragment"
         app:popUpTo="@id/splashFragment"
         app:popUpToInclusive="true"/>
</fragment>

在你的 Kotlin Splash Fragment 中:
private lateinit var navController: NavController

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
      super.onViewCreated(view, savedInstanceState)

      navController = Navigation.findNavController(view)

}

private fun navigateToMainFrag() {
      navController.navigate(R.id.action_splashFragment_to_mainFragment)
}

现在,当您按下“返回”按钮时,它会关闭应用程序而不是显示启动画面。

2

如果有人想纯粹用代码来实现这个:

原始答案 - 翻译为“最初的回答”

Navigation.findNavController(v)
.navigate(R.id.action_splashFragment_to_userProfileFragment2, null, 
new NavOptions.Builder().setPopUpTo(R.id.splashFragment, true).build())

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接