J'ai 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" />
Ce que je veux, c'est lorsque la deuxième action est déclenchée, je veux effacer la pile arrière et définir uniquement loginFragment pour rester dans la pile.
juste un problème est quand j'exécute l'Action2, 'slide_out_right' est exécuté comme animation de sortie
Je comprends que si nous éclatons le fragment de la pile, le "popExitAnim" de l'action1 sera déclenché au lieu de "exitAnim" de l'action2.
mais je veux savoir comment puis-je faire en sorte que le fragment exécute une animation slide_out_left pour quitter et également le sortir de la pile.
J'ai fini par remplacer onCreateAnimation
dans le fragment qui appelle navigate
. Cet exemple montre comment naviguer dans les graphiques de navigation imbriqués par ID et remplacer conditionnellement l'animation de sortie pop (ou popExitAnim
).
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)
}
Notez que cette situation particulière est en partie due à la nature directionnelle des animations de diapositives.
C'est un peu difficile à résoudre car NavOptions
est géré en interne par les méthodes pratiques utilisées pour lier votre tiroir au graphique de navigation. À l'origine, j'ai testé cette solution avec le menu des paramètres et onOptionsItemSelected
mais l'idée de base devrait également fonctionner ici.
Tout d'abord, assurez-vous que vos ID d'élément de menu correspondent à ceux de vos fragments de navigation:
<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>
Maintenant, plutôt que d'utiliser les méthodes prédéfinies pour connecter le tiroir à votre NavController Implémenter NavigationView.OnNavigationItemSelectedListener
dans votre activité NavHost et remplacez la méthode onNavigationItemSelected
comme ceci:
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
NavHost navHost = Navigation.findNavController(this, R.id.your_nav_Host_fragment);
return NavigationUI.onNavDestinationSelected(item, navHost);
}
Cela transmettra la sélection comme une navigation dans votre graphique. Remplacer your_nav_Host_fragment
avec l'ID de fragment sur lequel vous définissez app:defaultNavHost="true"
.
Vous remarquerez que bien que cela fonctionne, il reste par défaut les animations de diapositives. En effet, l'appel NavigationUI
crée en interne son propre NavOptions
avec ces paramètres:
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);
Malheureusement, la méthode ne prend pas encore de NavOptions.Builder
comme argument, mais vous pouvez créer une classe utilitaire basée sur le code source Android pour imiter la fonctionnalité:
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;
}
}
Enfin, dans votre activité, vous pouvez maintenant remplacer l'appel à NavigationUI par celui implémenté dans 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);
}
Cela devrait vous permettre de modifier les animations de transition de tiroir selon vos goûts sans avoir à remplacer le composant Navigation.