多模組專案的導覽最佳做法

導覽圖可包含以下項目的任意組合:

  • 單一目的地,例如 <fragment> 目的地。
  • 一個巢狀圖,當中包含一組相關目的地。
  • 一個 <include> 元素,可讓您以類似巢狀結構的方式嵌入另一個導覽圖檔案。

這種靈活性可讓您結合較小的導覽圖, 構成應用程式的完整導覽圖 圖表由不同的模組提供。

就本主題中的範例而言 聚焦於功能模組 至少一項功能 提供單一導覽圖,用來封裝所有目的地 以便實作這項功能在正式版應用程式中 較低層級的子模組,為這個較高層級的實作詳細資料 功能模組。每項功能模組都會直接或顯示在層級中 間接參與 app 模組。範例 在本文件中使用的多模組應用程式中 以下結構:

多模組應用程式範例的依附元件圖
範例應用程式的起始目的地
圖 1.應用程式架構和起始目的地 。

每個功能模組都是獨立單元,具有專屬導覽圖 以及目的地app 模組依附各個程式庫模組,因此您必須將程式庫模組做為實作詳細資料新增至其 build.gradle 檔案中,如下所示:

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

app 模組的功用

app 模組負責為您的 並將 NavHost 新增至 UI。在 app 模組的 您可以藉由使用 <include>。雖然 <include> 的功用與巢狀圖相同 <include> 支援來自其他專案模組或程式庫的圖表 如以下範例所示:

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

將程式庫納入頂層導覽圖後,您可以視需要瀏覽至程式庫圖表。舉例來說,您可以建立一項動作,以便從導覽圖中的某個片段瀏覽至設定圖表,如下所示:

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

多個功能模組需要參照一組通用時 到達網頁 (例如登入圖表) 請勿納入這些目的地 各項功能模組的導覽圖中常見的目的地。而應改為將這些常用目的地新增至 app 模組的導覽圖中。這樣每個功能模組就能進行跨功能模組導覽 即可導航到這些常用目的地

在上述範例中,動作指定的導覽目的地為 @id/settings_nav_graph。這個 ID 是指隨附圖表 @navigation/settings_navigation. 中定義的目的地。

應用程式模組中的頂層導覽

導覽元件含有 NavigationUI 類別。這個類別包含多種靜態方法,用於管理頂端應用程式列、導覽匣和底部導覽的導覽行為。如果您應用程式的 頂層目的地是由功能提供的 UI 元素組成 app 模組是自然將頂層放在 導覽和 UI 元素由於應用程式模組取決於 共同協作功能模組,讓所有目的地都能存取 擷取自應用程式模組定義的程式碼這表示如果項目 ID 與目的地 ID 相符,您就可以使用 NavigationUI 將目的地連結至選單項目

在圖 2 中,範例 app 模組的主要活動中定義了 BottomNavigationView。選單中的選單項目 ID 與程式庫圖表的導覽圖 ID 相符:

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

如要讓 NavigationUI 處理底部導覽,請透過主活動類別中的 onCreate() 呼叫 setupWithNavController(),如以下範例所示:

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

設定好這個程式碼後,當使用者點選底部導覽項目時,NavigationUI 就會瀏覽至適當的程式庫圖表。

請注意,一般來說,最好讓應用程式模組 必須硬性依賴內嵌在深層內部的特定目的地 您的功能模組導覽圖在大多數情況下,您會希望 模組,瞭解任何內嵌或內含的進入點 導覽圖 (這適用於功能模組以外的情況)。如要連結至程式庫導覽圖深處的目的地,建議您使用深層連結。如要讓程式庫瀏覽至其他程式庫導覽圖中的目的地,深層連結也是唯一的做法。

跨功能模組導覽

在編譯期間,獨立功能模組看不到彼此, 因此無法使用 ID 導覽至其他模組中的目的地。請改為使用深層連結,直接導覽至與隱含深層連結相關聯的目的地。

延續上一個範例,假設您需要從 :feature:home 模組至 :feature:settings 中的巢狀目的地 後續課程我們將逐一介紹 預先訓練的 API、AutoML 和自訂訓練只要在設定中新增連往該到達網頁的深層連結即可 導覽圖,如下所示:

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

接著將下列程式碼新增至首頁中按鈕的 onClickListener 片段:

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

與使用動作或目的地 ID 進行瀏覽不同,您可以瀏覽至所有圖表中的任何 URI,甚至可以跨模組進行瀏覽。

使用 URI 進行瀏覽時,系統「不會」重設返回堆疊。這項行為與明確深層連結導覽不同。在明確深層連結導覽流程中,返回堆疊會在導覽時遭取代。