I have 2 actions
Action1
<action
android:id="@+id/actionBaseFragmentToAskForLocation"
app:destination="@+id/introAskForLocationFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
Action2
<action
android:id="@+id/actionIntroAskLocationToLogin"
app:destination="@id/loginFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_right"
app:popExitAnim="@anim/fade_out"
app:popUpTo="@+id/app_main_navigation" />
What i want is when the second action is triggered i want to clear the back stack and set only loginFragment to remain in the stack.
just one problem is when i perform the Action2, 'slide_out_right' is performed as exit animation
I understand that if we pop the fragment from the stack the 'popExitAnim' of action1 will be triggered instead of 'exitAnim' of action2.
but i want to know how can I make the fragment perform slide_out_left animation for exiting and also pop it out of the stack.
I ended up overriding onCreateAnimation
in the fragment that calls navigate
. This exemple shows how to navigate through nested nav graphs by ID and replace the pop exit animation (or popExitAnim
) conditionnally.
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
val navController = findNavController()
val graph = navController.graph.findNode(R.id.onboardingGraph) as NavGraph
val dest = graph.findNode(R.id.confirmationFragment)
if (!enter && dest != null && navController.currentDestination?.id == dest.id) {
return AnimationUtils.loadAnimation(requireContext(), R.anim.slide_out_left)
}
return super.onCreateAnimation(transit, enter, nextAnim)
}
Note that this particular situation is partly due to the directional nature of slide animations.
This is bit of a tough one to solve due to NavOptions
being internally handled by the convenience methods used in binding your drawer to the navigation graph. I originally tested this solution with the settings menu and onOptionsItemSelected
but the basic idea should work here as well.
First, make sure your menu item IDs correspond to those of your navigation fragments:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
...
<item android:id="@+id/example_id" ... />
</menu>
<navigation xmlns:android="http://schemas.android.com/apk/res/android" ... >
...
<fragment android:id="@+id/example_id" ... />
</navigation>
Now, rather than using the ready-made methods for connecting the drawer to your NavController Implement NavigationView.OnNavigationItemSelectedListener
in your NavHost activity and override the method onNavigationItemSelected
like so:
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
NavHost navHost = Navigation.findNavController(this, R.id.your_nav_host_fragment);
return NavigationUI.onNavDestinationSelected(item, navHost);
}
This will forward the selection as a navigation in your graph. Replace your_nav_host_fragment
with the fragment ID on which you set app:defaultNavHost="true"
.
You will notice that while this works, it still defaults to the slide animations. This is because the NavigationUI
call internally creates its own NavOptions
with these settings:
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);
Unfortunately the method does not yet take a NavOptions.Builder
as an argument, but you can create an utility class based on the Android source code to mimic the functionality:
public class NavigationUIHelper {
public static boolean onNavDestinationSelected(@NonNull MenuItem item,
@NonNull NavController navController,
@NonNull NavOptions.Builder builder) {
if ((item.getOrder() & Menu.CATEGORY_SECONDARY) == 0) {
NavDestination destination = findStartDestination(navController.getGraph());
builder.setPopUpTo(destination.getId(), false);
}
NavOptions options = builder.build();
try {
navController.navigate(item.getItemId(), null, options);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
// Need to copy this private method as well
private static NavDestination findStartDestination(@NonNull NavGraph graph) {
NavDestination startDestination = graph;
while (startDestination instanceof NavGraph) {
NavGraph parent = (NavGraph) startDestination;
startDestination = parent.findNode(parent.getStartDestination());
}
return startDestination;
}
}
Finally, in your activity you can now replace the call to NavigationUI with the one implemented in NavigationUIHelper:
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
NavHost navHost = Navigation.findNavController(this, R.id.your_nav_host_fragment);
NavOptions.Builder builder = new NavOptions.Builder()
.setLaunchSingleTop(true)
.setEnterAnim(R.anim.custom_enter)
.setExitAnim(R.anim.custom_exit)
.setPopEnterAnim(R.anim.custom_pop_enter)
.setPopExitAnim(R.anim.custom_pop_exit);
return NavigationUIHelper.onNavDestinationSelected(item, navHost, builder);
}
This should allow you to change the drawer transition animations according to your liking without having to replace the Navigation component.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With