web-dev-qa-db-fra.com

Android Composant de navigation pop au problème de transition

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.

10
jaydeep_gedia

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.

6
maxbeaudoin

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.

2
Jonatan