导航到Android中的另一个片段后如何清除导航堆栈

Posted

技术标签:

【中文标题】导航到Android中的另一个片段后如何清除导航堆栈【英文标题】:How to clear navigation Stack after navigating to another fragment in Android 【发布时间】:2018-11-04 00:01:14 【问题描述】:

我在 android 中使用新的 Navigation Architecture Component,但在移动到新片段后,我卡在清除导航堆栈。

示例: 我在 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);

【问题讨论】:

你可以使用popBackStack或者不要添加LoginFragment到backstack提供nulladdToBackStack(null);并用新的Fragment替换它 我认为@Yupi 提供了一个很好的建议。或者您可以使用navigate() 之类的navigate(int resId, Bundle args, NavOptions navOptions) 方法并提供最适合您的场景的NavOptions 我尝试使用 NavOptions 但后退按钮仍将我送回 loginFragment 在导航图中,您可以为 homeFragment 操作添加 app:popUpTo="@+id/desiredFragment",当用户单击返回时,他将导航到所需的片段而不是登录片段 @Alex 我试过这样做,但没有任何效果。 【参考方案1】:

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

请参阅docs 了解更多信息。

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

【讨论】:

最佳答案,因为 clearTask 现在在新版本的 Android 导航组件中已弃用! 也可以像findNavController(fragment).navigate(R.id.action_signInFragment_to_usersFragment)那样使用action的id 我认为这个答案的关键在于“popUpTo”(删除当前片段和传递给此参数的第一次出现的 id 之间的所有片段)设置为 导航图本身的id。有效地彻底清除 backstack。仅添加此评论是因为我还没有看到它完全像这样解释,而这正是我想要的。 +1! 注意:并不是说如果你没有包含安全参数 gradle 就不会生成“Directions”类。欲了解更多信息,请访问here 有人调查了为什么“如果您使用方法 navigate(@IdRes int resId) 进行导航,您将无法获得所需的结果。” ?【参考方案2】:

我认为您的问题特别涉及如何使用 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"/>

【讨论】:

如何以编程方式而不是在 XML 中执行此操作?【参考方案3】:

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

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

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

【讨论】:

什么是fragment_apps?? 当前片段。这就像如果你在 fragmentOne 上并且想要去 fragmentTwo 你也需要使用 navController.popBackStack(R.id.fragmentOne, true); navController.navigate(R.id.fragmentTwo); 这是唯一对我有用的东西(导航 v2.3.5)。【参考方案4】:

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

此方法已弃用。将 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"

【讨论】:

您应该使用图根的 id,因为 clearTask xml 属性化的消息说:“将 popUpTo 设置为图的根并使用 popUpToInclusive”。我已经这样做了,并使用 findNavController().navigate(@IdRes resId, bundle) 实现了相同的期望行为 使用设置 popUpTo 和 popUpToInclusive 的推荐方法似乎不适用于起始目的地以外的目的地之间的操作:issuetracker.google.com/issues/116831650 我实际上遇到了一个问题,它不适用于起始目的地,令人沮丧。 如果您不知道要弹出到的片段,您会怎么做?例如,我有一个创建新项目的片段。创建后,我想显示该项目的详细信息视图。但我不希望用户在之后弹回创建片段,而是弹回前一个视图。这可以是所有项目的列表,也可以是新项目所基于的不同项目。那么 clearTask 是唯一的选择吗? 不,实际上不应该再使用它了。相反,您应该使用 popUpTo 包容性。我会很快用我对 popUpTo 使用的发现来更新答案。【参考方案5】:

感谢How to disable UP in Navigation for some fragment with the new Navigation Architecture Component?,我终于弄明白了

我必须将 .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());

                    

                
            );

【讨论】:

很高兴看到您能够实施我使用navigate(int resId, Bundle args, NavOptions navOptions)NavOptions 的建议 坏到坏的风格想法 是的,“setClearTask”已被弃用,但您可以使用:val navOptions = NavOptions.Builder().setPopUpTo(R.id.my_current_frag, true).build()findNavController().navigate(R.id.my_next_frag, null, navOptions) @c-它应该可以工作。可能您使用了错误的目标 FragmentId(例如,R.id.my_next_frag 可能类似于登录或启动屏幕);如果你只想弹出一个,你可以使用:NavHostFragment.findNavController(this).popBackStack() @PabloReyes 这是正确答案。只需指定最后删除的片段: 假设您有这个堆栈: Fragment_1 Fragment_2 Fragment_3 您现在在 Fragment_3 上并且想要导航到 Fragment_4 并清除所有其他片段。只需在导航 xml 中指定:app:popUpTo="@id/Fragment_1" app:popUpToInclusive="true"【参考方案6】:
    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);
    

【讨论】:

【参考方案7】:

这是我完成它的方法。

 //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)

【讨论】:

为什么你的inclusive标志false 根据documentationinclusive boolean: true to also pop the given destination from the back stack所以,我将inclusive设置为false,因为我不想从堆栈中弹出当前片段。【参考方案8】:

使用此代码

navController.navigateUp();

然后调用新的片段 安卓4.1.2版

【讨论】:

【参考方案9】:

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

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

这是我可以成功清除完整堆栈的唯一方法。 Google 确实需要让这一切变得更简单。

【讨论】:

这非常有用。我已经在 XML 中定义了 popUpTo 和 popUpToInclusive 并使用 NavDirections 而不是 ID 进行导航,但有时它仍然失败。使用后退/向上导航将用户重新调整到原始起始目的地:(【参考方案10】:

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

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()
  


【讨论】:

【参考方案11】:

对于

// 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>)

【讨论】:

【参考方案12】:

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

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

【讨论】:

【参考方案13】:

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

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()
    )

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

【讨论】:

【参考方案14】:

你可以这样做:

getFragmentManager().popBackStack();

如果你想检查计数,你可以这样做:

getFragmentManager().getBackStackEntryCount()

【讨论】:

以上是关于导航到Android中的另一个片段后如何清除导航堆栈的主要内容,如果未能解决你的问题,请参考以下文章

Android - 如何更改导航抽屉中的片段

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

Android导航组件:如何保存片段状态

热门从另一个片段导航到主页片段

如何在Android中的主/细分片段之间进行适当的导航?

如何在android中以编程方式在片段之间导航?