导航到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提供null
到addToBackStack(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 Task
和 Launch 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中的另一个片段后如何清除导航堆栈的主要内容,如果未能解决你的问题,请参考以下文章