Best practice per la navigazione per i progetti con più moduli

Un grafico di navigazione può essere costituito da una qualsiasi combinazione dei seguenti elementi:

  • Una destinazione singola, ad esempio una destinazione <fragment>.
  • Un grafico nidificato che incapsula un insieme di destinazioni correlate.
  • Un elemento <include>, che consente di incorporare un altro file di grafico di navigazione come se fosse nidificato.

Questa flessibilità ti consente di combinare grafici di navigazione più piccoli per formare il grafico di navigazione completo della tua app, anche se i grafici di navigazione più piccoli sono forniti da moduli separati.

Per gli esempi in questo argomento, ogni modulo di funzionalità è incentrato su una funzionalità e fornisce un singolo grafico di navigazione che incapsula tutte le destinazioni necessarie per implementare questa funzionalità. In un'app di produzione, potresti avere molti sottomoduli di livello inferiore che rappresentano i dettagli di implementazione di questo modulo delle funzionalità di livello superiore. Ciascuno di questi moduli delle funzionalità è incluso, direttamente o indirettamente, nel tuo modulo app. L'applicazione multi-modulo di esempio utilizzata in questo documento ha la seguente struttura:

grafico delle dipendenze per un&#39;applicazione multimodulo di esempio
la destinazione iniziale dell&#39;app di esempio
Figura 1. Architettura dell'app e destinazione iniziale per l'app di esempio.

Ogni modulo funzionalità è un'unità autonoma con un proprio grafico di navigazione e destinazioni. Il modulo app dipende da ciascun modulo e li aggiunge come dettagli di implementazione nel file build.gradle, come mostrato di seguito:

Trendy

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"))

Il ruolo del modulo app

Il modulo app ha la responsabilità di fornire il grafico completo della tua app e di aggiungere NavHost alla UI. Nel grafico di navigazione del modulo app, puoi fare riferimento ai grafici delle librerie utilizzando <include>. Sebbene <include> sia funzionalmente come l'utilizzo di un grafico nidificato, <include> supporta grafici di altri moduli di progetto o di progetti di biblioteche, come mostrato nell'esempio seguente:

<?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>

Una volta inclusa una libreria nel grafico di navigazione di primo livello, puoi spostarti nei grafici della libreria, se necessario. Ad esempio, potresti creare un'azione per accedere al grafico delle impostazioni da un frammento del grafico di navigazione, come mostrato di seguito:

<?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>

Quando più moduli delle funzionalità devono fare riferimento a un insieme comune di destinazioni, ad esempio un grafico di accesso, non devi includere queste destinazioni comuni nel grafico di navigazione di ciascun modulo delle funzionalità. Aggiungi invece le destinazioni comuni al grafico di navigazione del modulo app. Ogni modulo delle funzionalità può quindi navigare tra i moduli delle caratteristiche per raggiungere le destinazioni comuni.

Nell'esempio precedente, l'azione specifica una destinazione di navigazione di @id/settings_nav_graph. Questo ID si riferisce a una destinazione definita nel grafico incluso @navigation/settings_navigation.

Navigazione di primo livello nel modulo dell'app

Il componente Navigazione include una classe NavigationUI. Questa classe contiene metodi statici che gestiscono la navigazione con la barra delle app in alto, il riquadro di navigazione a scomparsa e la navigazione in basso. Se le destinazioni di primo livello dell'app sono composte da elementi UI forniti da moduli di funzionalità, nel modulo app puoi inserire gli elementi dell'interfaccia utente e di navigazione di primo livello. Poiché il modulo dell'app dipende dai moduli delle funzionalità che collaborano, tutte le destinazioni sono accessibili dal codice definito all'interno del modulo dell'app. Ciò significa che puoi utilizzare NavigationUI per collegare le destinazioni alle voci di menu se l'ID dell'articolo corrisponde all'ID di una destinazione.

Nella Figura 2, il modulo app di esempio definisce un elemento BottomNavigationView nella sua attività principale. Gli ID delle voci di menu nel menu corrispondono agli ID dei grafici di navigazione dei grafici delle librerie:

<?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>

Per consentire a NavigationUI di gestire la navigazione in basso, chiama setupWithNavController() da onCreate() nel tuo corso di attività principale, come mostrato nel seguente esempio:

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);
}

Una volta inserito questo codice, NavigationUI passa al grafico della libreria appropriato quando l'utente fa clic su un elemento di navigazione nella parte inferiore.

Tieni presente che in genere è una cattiva pratica che il tuo modulo dell'app abbia una dipendenza rigida da una destinazione specifica incorporata in profondità nel grafico di navigazione dei moduli delle funzionalità. Nella maggior parte dei casi è consigliabile che il modulo dell'app conosca solo il punto di accesso a eventuali grafici di navigazione incorporati o inclusi (questo si applica anche al di fuori dei moduli delle funzionalità). Se devi inserire un link a una destinazione che si trova all'interno del grafico di navigazione della libreria, il modo migliore per farlo è utilizzare un link diretto. I link diretti sono anche l'unico modo in cui una libreria può raggiungere una destinazione nel grafico di navigazione di un'altra libreria.

Spostarsi tra i moduli delle funzionalità

Al momento della compilazione, i moduli delle funzionalità indipendenti non sono visibili, quindi non puoi utilizzare gli ID per passare alle destinazioni in altri moduli. Utilizza un link diretto per passare direttamente a una destinazione associata a un link diretto implicito.

Proseguendo con l'esempio precedente, immagina di dover passare da un pulsante nel modulo :feature:home a una destinazione nidificata nel modulo :feature:settings. A questo scopo, aggiungi un link diretto alla destinazione nel grafico di navigazione delle impostazioni, come mostrato di seguito:

<?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>

Quindi aggiungi il codice seguente al onClickListener del pulsante nel frammento home:

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);
    }
});

A differenza della navigazione usando ID azione o destinazione, puoi accedere a qualsiasi URI di qualsiasi grafico, anche tra moduli.

Durante la navigazione utilizzando l'URI, lo stack di backup non viene reimpostato. Questo comportamento è diverso dalla navigazione con link diretti espliciti, in cui lo stack posteriore viene sostituito durante la navigazione.