拦截 NavigationUI.onNavDestinationSelected() 以使用 "inclusive = true" 使 backstack 弹出
Intercept NavigationUI.onNavDestinationSelected() to make backstack pop with "inclusive = true"
我已经通过 navigation_view.setupWithNavController(navController)
连接了我的菜单 xml 和 Android 导航组件,并且一切正常。我的菜单的一部分是注销选项。如果用户选择此导航目的地,应用程序应导航到 AuthorizationFragment
并将返回堆栈弹出到此 Fragment 并将 inclusive 设置为 true 以删除所有先前的片段。
我查看了导航库的代码,发现应该发生这种情况:
NavigationUI.onNavDestinationSelected():
/**
* Attempt to navigate to the {@link NavDestination} associated with the given MenuItem. This
* MenuItem should have been added via one of the helper methods in this class.
*
* <p>Importantly, it assumes the {@link MenuItem#getItemId() menu item id} matches a valid
* {@link NavDestination#getAction(int) action id} or
* {@link NavDestination#getId() destination id} to be navigated to.</p>
* <p>
* By default, the back stack will be popped back to the navigation graph's start destination.
* Menu items that have <code>android:menuCategory="secondary"</code> will not pop the back
* stack.
*
* @param item The selected MenuItem.
* @param navController The NavController that hosts the destination.
* @return True if the {@link NavController} was able to navigate to the destination
* associated with the given MenuItem.
*/
public static boolean onNavDestinationSelected(@NonNull MenuItem item,
@NonNull NavController navController) {
NavOptions.Builder builder = new NavOptions.Builder()
.setLaunchSingleTop(true)
.setEnterAnim(R.anim.nav_default_enter_anim)
.setExitAnim(R.anim.nav_default_exit_anim)
.setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
.setPopExitAnim(R.anim.nav_default_pop_exit_anim);
if ((item.getOrder() & Menu.CATEGORY_SECONDARY) == 0) {
builder.setPopUpTo(findStartDestination(navController.getGraph()).getId(), false);
}
NavOptions options = builder.build();
try {
//TODO provide proper API instead of using Exceptions as Control-Flow.
navController.navigate(item.getItemId(), null, options);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
所以我的问题是,是否有一种方法可以重写此方法并构建 NavOptions,这些 NavOptions 将返回堆栈弹出到包含选项设置为 true 的目标 ID?
像往常一样,睡过头有帮助。我发现您可以轻松设置自己的 NavigationItemSelectedListener
。另外,我在问题中提到的方法是 public 静态的,因此它也可以很容易地称为后备功能。
这是我的代码:
navigation_view.setNavigationItemSelectedListener { item: MenuItem ->
// check for my special case where I want to pop everything
// from the backstack until we reach the login fragment
if (item.itemId == R.id.fragment_auth) {
val options = NavOptions.Builder()
.setPopUpTo(navController.currentDestination!!.id, true)
.setLaunchSingleTop(true)
.build()
navController.navigate(R.id.action_global_authFragment, null, options)
true // return true -> we handled this navigation event
} else {
// Fallback for all other (normal) cases.
val handled = NavigationUI.onNavDestinationSelected(item, navController)
// This is usually done by the default ItemSelectedListener.
// But there can only be one! Unfortunately.
if (handled) drawer_layout.closeDrawer(navigation_view)
// return the result of NavigationUI call
handled
}
}
我已经通过 navigation_view.setupWithNavController(navController)
连接了我的菜单 xml 和 Android 导航组件,并且一切正常。我的菜单的一部分是注销选项。如果用户选择此导航目的地,应用程序应导航到 AuthorizationFragment
并将返回堆栈弹出到此 Fragment 并将 inclusive 设置为 true 以删除所有先前的片段。
我查看了导航库的代码,发现应该发生这种情况:
NavigationUI.onNavDestinationSelected():
/**
* Attempt to navigate to the {@link NavDestination} associated with the given MenuItem. This
* MenuItem should have been added via one of the helper methods in this class.
*
* <p>Importantly, it assumes the {@link MenuItem#getItemId() menu item id} matches a valid
* {@link NavDestination#getAction(int) action id} or
* {@link NavDestination#getId() destination id} to be navigated to.</p>
* <p>
* By default, the back stack will be popped back to the navigation graph's start destination.
* Menu items that have <code>android:menuCategory="secondary"</code> will not pop the back
* stack.
*
* @param item The selected MenuItem.
* @param navController The NavController that hosts the destination.
* @return True if the {@link NavController} was able to navigate to the destination
* associated with the given MenuItem.
*/
public static boolean onNavDestinationSelected(@NonNull MenuItem item,
@NonNull NavController navController) {
NavOptions.Builder builder = new NavOptions.Builder()
.setLaunchSingleTop(true)
.setEnterAnim(R.anim.nav_default_enter_anim)
.setExitAnim(R.anim.nav_default_exit_anim)
.setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
.setPopExitAnim(R.anim.nav_default_pop_exit_anim);
if ((item.getOrder() & Menu.CATEGORY_SECONDARY) == 0) {
builder.setPopUpTo(findStartDestination(navController.getGraph()).getId(), false);
}
NavOptions options = builder.build();
try {
//TODO provide proper API instead of using Exceptions as Control-Flow.
navController.navigate(item.getItemId(), null, options);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
所以我的问题是,是否有一种方法可以重写此方法并构建 NavOptions,这些 NavOptions 将返回堆栈弹出到包含选项设置为 true 的目标 ID?
像往常一样,睡过头有帮助。我发现您可以轻松设置自己的 NavigationItemSelectedListener
。另外,我在问题中提到的方法是 public 静态的,因此它也可以很容易地称为后备功能。
这是我的代码:
navigation_view.setNavigationItemSelectedListener { item: MenuItem ->
// check for my special case where I want to pop everything
// from the backstack until we reach the login fragment
if (item.itemId == R.id.fragment_auth) {
val options = NavOptions.Builder()
.setPopUpTo(navController.currentDestination!!.id, true)
.setLaunchSingleTop(true)
.build()
navController.navigate(R.id.action_global_authFragment, null, options)
true // return true -> we handled this navigation event
} else {
// Fallback for all other (normal) cases.
val handled = NavigationUI.onNavDestinationSelected(item, navController)
// This is usually done by the default ItemSelectedListener.
// But there can only be one! Unfortunately.
if (handled) drawer_layout.closeDrawer(navigation_view)
// return the result of NavigationUI call
handled
}
}