在某些情况下,使用导航体系结构组件,我们还可以从这样的代码进行动态导航:
navController.navigate(R.id.cartFragment)
然后,当我们需要从该目标导航到新的根目标(后向导航将关闭应用程序)时,我们无法知道当前的根目标是什么,因此我们不知道如何设置popUpTo目的地。
val navigationOptions = NavOptions.Builder().setPopUpTo(R.id.???, true).build()
navController.navigate(R.id.loginFragment, null, navigationOptions)
如果将其设置为不位于后退栈中的目标,则将获得警告日志(从popBackStackInternal
中的NavController
):
“正在将popBackStack忽略到目标*:id / splashFragment,因为它在当前后退堆栈中找不到”
之所以会发生这种情况,是因为流中还有很多情况,我们将PopUpTo设置为取决于流,因此我们有不同的根目标。
我已经查看了NavController
上所有可用的方法,并尝试了对mBackStack
的一些思考,但是我无法找到清除后栈的方法。
在不知道当前根目录目的地的情况下如何清除后堆栈?
编辑:添加了导航图示例
<fragment
android:id="@+id/navigation_cart"
android:name="ProjectsFragment_"
android:label="Cart"
tools:layout="@layout/fragment_cart" />
<fragment
android:id="@+id/loginFragment"
android:name="LoginFragment_"
android:label="Login"
tools:layout="@layout/fragment_login">
<--! Dynamic navigation to either Consent or Cart based on state -->
<action
android:id="@+id/action_login_to_home"
app:destination="@id/navigation_cart"
app:popUpTo="@id/loginFragment"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_loginFragment_to_consentFragment"
app:destination="@id/consentFragment" />
<action
android:id="@+id/action_loginFragment_to_contactFragment"
app:destination="@id/contactFragment" />
</fragment>
<fragment
android:id="@+id/splashFragment"
android:name="SplashFragment_"
android:label="SplashFragment"
tools:layout="@layout/fragment_splash">
<--! Dynamic navigation to either Onboarding, Login or Cart based on state -->
</fragment>
<dialog
android:id="@+id/consentFragment"
android:name="ConsentFragment"
android:label="ConsentFragment"
tools:layout="@layout/fragment_consent" />
<fragment
android:id="@+id/onboardingIntroFragment"
android:name="OnboardingIntroFragment_"
android:label="OnboardingIntroFragment"
tools:layout="@layout/fragment_onboarding">
<action
android:id="@+id/action_onboardingIntroFragment_to_loginFragment"
app:destination="@id/loginFragment"
app:popUpTo="@id/onboardingIntroFragment"
app:popUpToInclusive="true" />
</fragment>
<dialog
android:id="@+id/contactFragment"
android:name="ContactFragment"
android:label="ContactFragment"
tools:layout="@layout/fragment_contact" />
关于上述SplashFragment中的具体情况,我们将根据状态动态导航到“入职”,“登录”或“购物车”。如果是这样,我们导航到“登录”,成功登录后,动态导航到“购物车”。
我在一般动态导航的两种情况下都不知道我们的根目录是什么,尽管不能正确设置popUpTo。从Splash调用时为SplashFragment,从Login调用时为LoginFragment,或者在结帐时出现的另一种情况可能是CartFragment或作为根目标的另一个片段。
如何动态地确定根目标是什么,或者只是在导航中清除后退堆栈?
尽管这有点简化了,因为我们的应用程序中有更多相同模式的案例。
编辑2:根目标定义 我将根目标定义为popUpTo的目标(包括端点),以清除整个后退堆栈,否则后退操作将关闭应用程序。 例如,Splash-> Login将使用popUpTo清除飞溅,然后Login to Cart应清除其余的堆栈,但现在根目录不是Splash,而是Login,因为登录时弹出Splash。
答案 0 :(得分:0)
答案 1 :(得分:0)
我们最终有两种解决方法。
1):当我们可以从动态导航位置中找出当前的根目标是什么时,我们将该参数解析为动态导航器。
object Navigator {
fun showStartFragment(navController: NavController, @IdRes currentDestination: Int) {
val navigationOptions = NavOptions.Builder().setPopUpTo(currentDestination, true).build()
if (!Settings.Intro.onboardingCompleted) {
navController.navigate(R.id.onboardingIntroFragment, null, navigationOptions)
} else {
val isLoggedIn = Settings.User.loggedIn
if (isLoggedIn) {
MainActivity.shared?.mainStateHandler?.showNextFragment(navController, currentDestination)
} else {
navController.navigate(R.id.loginFragment, null, navigationOptions)
}
}
}
}
2),但是我们也遇到了这样的情况,可能会导致异步异步响应终端,用户必须注销,令牌过期或用户被阻止。为了解决这个问题,我们修改了NavController来获取反射的根目录。
fun NavController.getRootDestination(): NavDestination? {
val mBackStack = NavController::class.java.getDeclaredField("mBackStack").let {
it.isAccessible = true
return@let it.get(this) as Deque<*>
}
for (entry in mBackStack) {
val destination = Reflector.getMethodResult(entry, "getDestination") as NavDestination
if (destination !is NavGraph) {
return destination
}
}
return null
}
object Reflector {
fun getMethodResult(`object`: Any, methodName: String): Any? {
return `object`.javaClass.getMethod(methodName).let {
it.isAccessible = true
return@let it.invoke(`object`)
}
}
}