227

我在 android 中使用新的导航架构组件,并且在移动到新片段后我被困在清除导航堆栈中。

示例:我在 loginFragment 中,我希望在导航到主片段时从堆栈中清除此片段,以便用户在按下后退按钮时不会返回到 loginFragment。

我正在使用一个简单的NavHostFragment.findNavController(Fragment).navigate(R.id.homeFragment)进行导航。

当前代码:

mAuth.signInWithCredential(credential)
            .addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment);
                    } else {
                        Log.w(TAG, "signInWithCredential:failure", task.getException());
                    }
                }
            });

我尝试在navigate()中使用NavOptions,但后退按钮仍在将我送回 loginFragment

NavOptions.Builder navBuilder = new NavOptions.Builder();
NavOptions navOptions = navBuilder.setPopUpTo(R.id.homeFragment, false).build();   
             NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment, null, navOptions);
4

14 回答 14

345

首先,将属性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())

有关更多信息,请参阅文档

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

于 2018-08-22T20:17:47.743 回答
75

我认为您的问题特别涉及如何使用 Pop Behavior / Pop To / app:popUpTo (in xml)

在文档中,
在导航之前弹出到给定的目的地。这会从返回堆栈中弹出所有不匹配的目标,直到找到该目标。

示例(简单求职应用程序)
我的start_screen_nav图表是这样的:

startScreenFragment (start) -> loginFragment -> EmployerMainFragment

                            -> loginFragment -> JobSeekerMainFragment

如果我想导航到EmployerMainFragment并弹出所有内容 startScreenFragment,那么代码将是:

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/startScreenFragment"
            app:popUpToInclusive="true" />

如果我想导航到EmployerMainFragment并弹出所有排除 startScreenFragment,那么代码将是:

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/startScreenFragment"/>

如果我想导航到EmployerMainFragment并弹出loginFragment不是 startScreenFragment,那么代码将是:

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/loginFragment"
            app:popUpToInclusive="true"/>

或者

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/startScreenFragment"/>
于 2019-07-01T07:39:40.613 回答
61

在我的情况下,我需要在打开一个新片段之前删除后面 Stack 中的所有内容,所以我使用了这段代码

navController.popBackStack(R.id.fragment_apps, true);
navController.navigate(R.id.fragment_company);

第一行删除返回堆栈,直到它到达在我的例子中指定的片段,它是主片段,所以它完全删除了所有返回堆栈,当用户在 fragment_company 中单击返回时,他关闭了应用程序。

于 2019-07-27T22:33:31.473 回答
16

注意:清除任务已弃用,官方描述为

此方法已弃用。将 setPopUpTo(int, boolean) 与 NavController 图形的 id 一起使用,并将 inclusive 设置为 true。

旧答案

如果你不想在代码中经历所有这些模糊,你可以简单地签Clear TaskLaunch Options动作的属性。

启动选项

编辑:从 Android Studio 3.2 Beta 5 开始,Clear Task 在 Launch Options 窗口中不再可见,但您仍然可以在导航的 XML 代码中使用它,在action标签中,通过添加

app:clearTask="true"
于 2018-06-07T12:41:14.590 回答
10

我终于弄明白了,感谢如何使用新的导航架构组件在导航中禁用某些片段的 UP?

我必须将 .setClearTask(true) 指定为 NavOption。

mAuth.signInWithCredential(credential)
            .addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "signInWithCredential:success");


                        NavOptions.Builder navBuilder = new NavOptions.Builder();
                        NavOptions navOptions = navBuilder.setClearTask(true).build();
                        NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment,null,navOptions);
                    } else {
                        Log.w(TAG, "signInWithCredential:failure", task.getException());

                    }

                }
            });
于 2018-05-25T11:17:36.040 回答
10
    NavController navController 
    =Navigation.findNavController(requireActivity(),          
    R.id.nav_host_fragment);// initialize navcontroller

    if (navController.getCurrentDestination().getId() == 
     R.id.my_current_frag) //for avoid crash
  {
    NavDirections action = 
    DailyInfoSelectorFragmentDirections.actionGoToDestionationFragment();

    //for clear current fragment from stack
    NavOptions options = new 
    NavOptions.Builder().setPopUpTo(R.id.my_current_frag, true).build();
    navController.navigate(action, options);
    }
于 2019-09-29T07:02:59.453 回答
8

使用此代码

navController.navigateUp();

然后调用新的 Fragment android 版本 4.1.2

于 2021-01-29T17:56:14.377 回答
8

将在此处添加另一个答案,因为上述方法都不适合我……我们有多个导航图。

findNavController().navigate(R.id.dashboard_graph,null,NavOptions.Builder().setPopUpTo(findNavController().graph.startDestination, true).build())

这是我可以成功清除完整堆栈的唯一方法。谷歌真的需要让这更简单。

于 2021-04-27T10:35:01.557 回答
8

这是我完成它的方法。

 //here the R.id refer to the fragment one wants to pop back once pressed back from the newly  navigated fragment
 val navOption = NavOptions.Builder().setPopUpTo(R.id.startScorecardFragment, false).build()

//now how to navigate to new fragment
Navigation.findNavController(this, R.id.my_nav_host_fragment)
                    .navigate(R.id.instoredBestPractice, null, navOption)
于 2019-10-04T20:25:56.850 回答
4

您可以像这样覆盖基本活动的后按:

override fun onBackPressed() {

  val navigationController = nav_host_fragment.findNavController()
  if (navigationController.currentDestination?.id == R.id.firstFragment) {
    finish()
  } else if (navigationController.currentDestination?.id == R.id.secondFragment) {
    // do nothing
  } else {
    super.onBackPressed()
  }

}
于 2019-09-04T06:12:50.220 回答
2

在我使用带有NavigationView(菜单抽屉)的Navigation 组件的情况下:

1.

mNavController.popBackStack(R.id.ID_OF_FRAGMENT_ROOT_TO_POP, true)
    mNavController.navigate(
      R.id.DESTINATION_ID,
      null,
      NavOptions.Builder()
                .setPopUpTo(R.id.POP_TO_DESTINATION_ID, true)
                .build()
    )

单击侧面菜单抽屉上的注销后,我想清除堆栈!希望对某人有所帮助!

于 2021-10-07T11:15:49.513 回答
1

为了

// Navigation library
    def nav_version = "2.3.5"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

这个解决方案对我有用

 
findNavController().popBackStack(R.id.<Current Fragment Id In NavGraph>, true)

 
findNavController().navigate(R.id.< Your Destination Fragment  in NavGraph>)

于 2021-09-09T12:08:55.820 回答
0

我挣扎了一会儿,以防止后退按钮返回到我的开始片段,在我的情况下,这是一条只应该出现一次的介绍消息。

简单的解决方案是创建一个指向用户应该停留的目的地的全局动作。您必须app:popUpTo="..."正确设置 - 将其设置为您想要弹出的目的地。就我而言,这是我的介绍信息。还设置app:popUpToInclusive="true"

于 2019-12-09T21:38:32.780 回答
-6

您可以执行以下简单操作:

getFragmentManager().popBackStack();

如果要检查计数,可以执行以下操作:

getFragmentManager().getBackStackEntryCount()
于 2020-07-14T09:55:39.070 回答