Bonnes pratiques de navigation pour les projets multimodules

Un graphique de navigation peut être constitué de n'importe quelle combinaison des éléments suivants :

  • Une destination unique, telle qu'une destination <fragment>.
  • Un graphique imbriqué qui encapsule un ensemble de destinations associées.
  • Un élément <include>, qui vous permet d'intégrer un autre fichier de graphique de navigation comme s'il était imbriqué.

Cette flexibilité vous permet de combiner des graphiques de navigation plus petits pour former le graphique de navigation complet de votre application, même si ces graphiques plus petits sont fournis par des modules distincts.

Dans les exemples de cette rubrique, chaque module de fonctionnalité est axé sur une fonctionnalité et fournit un seul graphe de navigation qui encapsule toutes les destinations nécessaires pour mettre en œuvre cette fonctionnalité. Dans une application de production, vous pouvez avoir de nombreux sous-modules de niveau inférieur représentant des détails de mise en œuvre de ce module de fonctionnalité de niveau supérieur. Chacun de ces modules de fonctionnalité est inclus, directement ou indirectement, dans votre module app. L'exemple d'application multimodules utilisé dans ce document présente la structure suivante :

graphique de dépendances pour un exemple d&#39;application multimodules
la destination de départ de l&#39;exemple d&#39;application
Figure 1. Architecture de l'application et destination de départ pour l'exemple d'application.

Chaque module de fonctionnalité est une unité autonome dotée de son propre graphique de navigation et de ses propres destinations. Le module app dépend de chacun de ces éléments et les ajoute comme détails d'implémentation dans son fichier build.gradle, comme indiqué ci-dessous :

Groovy

dependencies {
    ...
    implementation project(":feature:home")
    implementation project(":feature:favorites")
    implementation project(":feature:settings")

Kotlin

dependencies {
    ...
    implementation(project(":feature:home"))
    implementation(project(":feature:favorites"))
    implementation(project(":feature:settings"))

Le rôle du module app

Le module app fournit le graphique complet de votre application et ajoute NavHost à votre UI. Dans le graphique de navigation du module app, vous pouvez référencer les graphiques de la bibliothèque en utilisant <include>. Bien que, d'un point de vue fonctionnel, l'utilisation de <include> soit identique à l'utilisation d'un graphique imbriqué, <include> prend en charge les graphiques provenant d'autres modules de projet ou de projets de bibliothèque, comme illustré dans l'exemple suivant :

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/home_nav_graph">

    <include app:graph="@navigation/home_navigation" />
    <include app:graph="@navigation/favorites_navigation" />
    <include app:graph="@navigation/settings_navigation" />
</navigation>

Une fois qu'une bibliothèque est incluse dans le graphique de navigation de premier niveau, vous pouvez accéder aux graphiques de la bibliothèque si nécessaire. Par exemple, vous pouvez créer une action permettant d'accéder au graphique des paramètres à partir d'un fragment de votre graphique de navigation, comme illustré ci-dessous :

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/home_nav_graph">

    <include app:graph="@navigation/home_navigation" />
    <include app:graph="@navigation/favorites_navigation" />
    <include app:graph="@navigation/settings_navigation" />

    <fragment
        android:id="@+id/random_fragment"
        android:name="com.example.android.RandomFragment"
        android:label="@string/fragment_random" >
        <!-- Launch into Settings Navigation Graph -->
        <action
            android:id="@+id/action_random_fragment_to_settings_nav_graph"
            app:destination="@id/settings_nav_graph" />
    </fragment>
</navigation>

Lorsque plusieurs modules de fonctionnalité doivent référencer un ensemble commun de destinations, comme un graphique de connexion, vous ne devez pas inclure ces destinations courantes dans le graphique de navigation de chaque module de fonctionnalité. Ajoutez plutôt ces destinations courantes au graphique de navigation de votre module app. Chaque module de fonctionnalité peut ensuite parcourir les différents modules de fonctionnalité pour accéder à ces destinations courantes.

Dans l'exemple précédent, l'action spécifie la destination de navigation @id/settings_nav_graph. Cet ID fait référence à une destination définie dans le graphique @navigation/settings_navigation. inclus.

Navigation de premier niveau dans le module de l'application

Le composant Navigation inclut une classe NavigationUI. Cette classe contient des méthodes statiques permettant de gérer la navigation avec la barre d'application supérieure, le panneau de navigation et la navigation inférieure. Si les destinations de premier niveau de votre application sont composées d'éléments d'interface utilisateur fournis par des modules de fonctionnalité, le module app est un emplacement naturel où placer les éléments de navigation et d'interface utilisateur de premier niveau. Étant donné que le module de l'application dépend des modules de fonctionnalité collaboratifs, toutes leurs destinations sont accessibles à partir du code défini dans le module de l'application. Cela signifie que vous pouvez utiliser NavigationUI pour lier des destinations aux éléments de menu si l'ID de l'élément correspond à celui d'une destination.

Dans la figure 2, l'exemple de module app définit un objet BottomNavigationView dans son activité principale. Les ID des éléments de menu correspondent aux ID du graphique de navigation des graphiques de la bibliothèque :

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@id/home_nav_graph"
        android:icon="@drawable/ic_home"
        android:title="Home"
        app:showAsAction="ifRoom"/>

    <item
        android:id="@id/favorites_nav_graph"
        android:icon="@drawable/ic_favorite"
        android:title="Favorites"
        app:showAsAction="ifRoom"/>

    <item
        android:id="@id/settings_nav_graph"
        android:icon="@drawable/ic_settings"
        android:title="Settings"
        app:showAsAction="ifRoom" />
</menu>

Pour permettre à NavigationUI de gérer la navigation inférieure, appelez setupWithNavController() à partir de onCreate() dans votre classe d'activité principale, comme illustré dans l'exemple suivant :

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHostFragment.navController

    findViewById<BottomNavigationView>(R.id.bottom_nav)
            .setupWithNavController(navController)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    NavHostFragment navHostFragment =
            (NavHostFragment) supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
    NavController navController = navHostFragment.getNavController();
    BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);

    NavigationUI.setupWithNavController(bottomNav, navController);
}

Une fois ce code en place, NavigationUI accède au graphique de bibliothèque approprié lorsque l'utilisateur clique sur un élément de navigation inférieure.

N'oubliez pas qu'il est généralement déconseillé que votre module d'application dépende fortement d'une destination spécifique intégrée en profondeur au graphique de navigation de vos modules de fonctionnalité. Dans la plupart des cas, vous souhaitez que votre module d'application ne connaisse que le point d'entrée des graphiques de navigation intégrés ou inclus (cela s'applique aussi en dehors des modules de fonctionnalité). Si vous avez besoin de créer un lien vers une destination profonde dans le graphique de navigation de votre bibliothèque, le moyen le plus simple consiste à utiliser un lien profond. Les liens profonds sont également le seul moyen pour une bibliothèque d'accéder à une destination dans le graphique de navigation d'une autre bibliothèque.

Parcourir les modules de fonctionnalité

Au moment de la compilation, les modules de fonctionnalité indépendants ne peuvent pas se voir les uns les autres. Vous ne pouvez donc pas vous servir des ID pour accéder à des destinations dans d'autres modules. Utilisez plutôt un lien profond pour accéder directement à une destination associée à un lien profond implicite.

Pour reprendre l'exemple précédent, imaginons que vous deviez naviguer d'un bouton du module :feature:home vers une destination imbriquée dans le module :feature:settings. Pour ce faire, ajoutez un lien profond à la destination dans le graphique de navigation des paramètres, comme indiqué ci-dessous :

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/settings_nav_graph"
    app:startDestination="@id/settings_fragment_one">

    ...

    <fragment
        android:id="@+id/settings_fragment_two"
        android:name="com.example.google.login.SettingsFragmentTwo"
        android:label="@string/settings_fragment_two" >

        <deepLink
            app:uri="android-app://example.google.app/settings_fragment_two" />
    </fragment>
</navigation>

Ajoutez ensuite le code suivant à la section onClickListener du bouton dans le fragment de la page d'accueil :

Kotlin

button.setOnClickListener {
    val request = NavDeepLinkRequest.Builder
        .fromUri("android-app://example.google.app/settings_fragment_two".toUri())
        .build()
    findNavController().navigate(request)
}

Java

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        NavDeepLinkRequest request = NavDeepLinkRequest.Builder
            .fromUri(Uri.parse("android-app://example.google.app/settings_fragment_two"))
            .build();
        NavHostFragment.findNavController(this).navigate(request);
    }
});

Contrairement à la navigation à l'aide d'ID d'action ou de destination, vous pouvez accéder à n'importe quel URI dans n'importe quel graphique, même entre différents modules.

Lorsque vous utilisez des URI, la pile "Retour" n'est pas réinitialisée. Ce comportement est différent de la navigation à l'aide de liens profonds explicites, où la pile "Retour" est remplacée lors de la navigation.