ナビゲーション グラフを設計する

Navigation コンポーネントでは、ナビゲーション グラフを使用してアプリのナビゲーションを管理します。ナビゲーション グラフとは、アプリ内の各デスティネーションと、それらの間の接続を含むデータ構造です。

デスティネーションのタイプ

デスティネーションの一般的なタイプとしては、ホストされているデスティネーション、アクティビティのデスティネーション、ダイアログのデスティネーションの 3 つがあります。次の表に、これら 3 つのデスティネーション タイプとその用途をまとめます。

タイプ

説明

使用場面

ホストされているデスティネーション

ナビゲーション ホスト全体を埋めるデスティネーションです。ホストされたデスティネーションのサイズはナビゲーション ホストのサイズと同じとなり、それ以前のデスティネーションは表示されなくなります。

メイン画面や詳細画面。

ダイアログのデスティネーション

オーバーレイ UI コンポーネントを表すデスティネーションです。この UI は、ナビゲーション ホストの場所やサイズには関連付けられません。それ以前のデスティネーションは、このデスティネーションの下に表示されます。

アラート、選択肢、フォーム。

アクティビティ

アプリ内の固有の画面または機能を表すデスティネーションです。

ナビゲーション グラフの終了ポイントとして機能し、Navigation コンポーネントとは別に管理されている新しい Android アクティビティを開始します。

最新の Android 開発では、アプリを 1 つのアクティビティで構成します。アクティビティのデスティネーションは、サードパーティ アクティビティとやり取りする場合や、移行プロセスの一部として使用するのが最適です。

このドキュメントでは、最も一般的で基本的なデスティネーションである「ホストされているデスティネーション」の例を示します。それ以外のデスティネーションについては、以下のガイドをご覧ください。

フレームワーク

どの場合にも同じ一般的なワークフローが適用されますが、ナビゲーション ホストとグラフの作成方法は使用する UI フレームワークによって異なります。

  • Compose で作成する: NavHost コンポーザブルを使用します。Kotlin DSL を使用して、NavGraph を追加します。グラフは次の 2 つの方法で作成できます。
    • NavHost の一部として作成する: NavHost を追加する際に、その一部としてナビゲーション グラフを直接作成します。
    • プログラマティックに作成する: NavController.createGraph() メソッドを使用して NavGraph を作成し、直接 NavHost に渡します。
  • フラグメント: ビュー UI フレームワークでフラグメントを使用する場合は、NavHostFragment をホストとして使用します。ナビゲーション グラフを作成する方法はいくつかあります。
    • プログラムを使用する場合: Kotlin DSL を使用して NavGraph を作成し、NavHostFragment に直接適用します。
      • Kotlin DSL で使用される createGraph() 関数は、フラグメントと Compose の両方で同じです。
    • XML で作成する: ナビゲーション ホストとグラフを XML で直接記述します。
    • Android Studio エディタ: Android Studio の GUI エディタを使用して、グラフを XML リソース ファイルとして作成、調整します。

文章

Compose で、シリアル化可能なオブジェクトまたはクラスを使用してルートを定義します。ルートには、デスティネーションへのルートが記述され、デスティネーションが必要とするすべての情報が含まれています。ルートを定義したら、NavHost コンポーザブルを使用してナビゲーション グラフを作成します。次の例を考えてみましょう。

@Serializable
object Profile
@Serializable
object FriendsList

val navController = rememberNavController()

NavHost(navController = navController, startDestination = Profile) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
    // Add more destinations similarly.
}
  1. シリアル化可能なオブジェクトは、2 つのルート(ProfileFriendsList)のそれぞれを表します。
  2. NavHost コンポーザブルの呼び出しは、NavController と開始デスティネーションへのルートを渡します。
  3. NavHost に渡されたラムダにより、最終的に NavController.createGraph() が呼び出され、NavGraph が返されます。
  4. 各ルートは、結果の NavGraph にデスティネーションを追加する NavGraphBuilder.composable<T>() の型引数として指定されます。
  5. composable に渡されたラムダが、NavHost がそのデスティネーションに対して表示するものです。

ラムダを理解する

NavGraph を作成するラムダについては、前のスニペットで作成したのと同じグラフを作成するためのものと考えると理解しやすいでしょう。NavController.createGraph() を使用して個別に NavGraph を作成し、直接 NavHost に渡すことができます。

val navGraph by remember(navController) {
  navController.createGraph(startDestination = Profile)) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
  }
}
NavHost(navController, navGraph)

引数を渡す

デスティネーションにデータを渡す必要がある場合は、パラメータを持つクラスでルートを定義します。たとえば、Profile ルートは name パラメータを持つデータクラスです。

@Serializable
data class Profile(val name: String)

このデスティネーションに引数を渡す必要がある場合は、ルートクラスのインスタンスを作成し、その引数をクラス コンストラクタに渡します。

ルート インスタンスを取得する

ルート インスタンスは、NavBackStackEntry.toRoute() または SavedStateHandle.toRoute() で取得できます。composable() を使用してデスティネーションを作成する場合、NavBackStackEntry をパラメータとして使用できます。

@Serializable
data class Profile(val name: String)

val navController = rememberNavController()

NavHost(navController = navController, startDestination = Profile(name="John Smith")) {
    composable<Profile> { backStackEntry ->
        val profile: Profile = backStackEntry.toRoute()
        ProfileScreen(name = profile.name) }
}

このスニペットの次の点に注意してください。

  • Profile ルートは、ナビゲーション グラフ内の開始デスティネーションを指定し、name の引数として "John Smith" を指定します。
  • デスティネーション自体は composable<Profile>{} ブロックです。
  • ProfileScreen コンポーザブルは、独自の name 引数として profile.name の値を取ります。
  • そのため、値 "John Smith"ProfileScreen に渡されます。

最も簡単な例

NavControllerNavHost が連携して動作する完全な例を以下に示します。

@Serializable
data class Profile(val name: String)

@Serializable
object FriendsList

// Define the ProfileScreen composable.
@Composable
fun ProfileScreen(
    profile: Profile
    onNavigateToFriendsList: () -> Unit,
  ) {
  Text("Profile for ${profile.name}")
  Button(onClick = { onNavigateToFriendsList() }) {
    Text("Go to Friends List")
  }
}

// Define the FriendsListScreen composable.
@Composable
fun FriendsListScreen(onNavigateToProfile: () -> Unit) {
  Text("Friends List")
  Button(onClick = { onNavigateToProfile() }) {
    Text("Go to Profile")
  }
}

// Define the MyApp composable, including the `NavController` and `NavHost`.
@Composable
fun MyApp() {
  val navController = rememberNavController()
  NavHost(navController, startDestination = Profile(name = "John Smith")) {
    composable<Profile> { backStackEntry ->
        val profile: Profile = backStackEntry.toRoute()
        ProfileScreen(
            profile = profile,
            onNavigateToFriendsList = {
                navController.navigate(route = FriendsList)
            }
        )
    }
    composable<FriendsList> {
      FriendsListScreen(
        onNavigateToProfile = {
          navController.navigate(
            route = Profile(name = "Aisha Devi")
          )
        }
      )
    }
  }
}

このスニペットでは、NavController をコンポーザブルに渡す代わりに、イベントを NavHost に公開しています。この場合、コンポーザブルには、NavHostNavController.navigate() を呼び出すラムダを渡すための () -> Unit 型のパラメータが必要となります。

フラグメント

前のセクションで説明したように、フラグメントを使用すると、Kotlin DSL、XML、または Android Studio エディタを使用して、プログラマティックにナビゲーション グラフを作成できます。

以降のセクションでは、これらの方法について詳しく説明します。

プログラマティック

Kotlin DSL を使用すると、フラグメントによるナビゲーション グラフをプログラマティックに作成できます。XML リソース ファイルを使用する方法に比べ、多くの点でより簡潔で先進的な方法です。

次の例で、2 画面のナビゲーション グラフを実装する場合を考えてみましょう。

まず、NavHostFragment を作成する必要があります。これには app:navGraph 要素を含めないでください。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>

次に、NavHostFragmentidNavController.findNavController に渡します。これにより、NavController が NavHostFragment に関連付けられます。

その後、NavController.createGraph() を呼び出すと、グラフが NavController にリンクされ、その結果として NavHostFragment にもリンクされます。

@Serializable
data class Profile(val name: String)

@Serializable
object FriendsList

// Retrieve the NavController.
val navController = findNavController(R.id.nav_host_fragment)

// Add the graph to the NavController with `createGraph()`.
navController.graph = navController.createGraph(
    startDestination = Profile(name = "John Smith")
) {
    // Associate each destination with one of the route constants.
    fragment<ProfileFragment, Profile> {
        label = "Profile"
    }

    fragment<FriendsListFragment, FriendsList>() {
        label = "Friends List"
    }

    // Add other fragment destinations similarly.
}

この方法での DSL の使用は、前の Compose のセクションで説明したワークフローと非常によく似ています。たとえば、どちらの場合も NavController.createGraph() 関数で NavGraph を生成します。同様に、NavGraphBuilder.composable() はコンポーザブルのデスティネーションをグラフに追加しますが、この場合は NavGraphBuilder.fragment() がフラグメント デスティネーションを追加します。

Kotlin DSL の使用方法については、NavGraphBuilder DSL でグラフを作成するをご覧ください。

XML

XML は自分で直接記述できます。次の例は、前のセクションの 2 画面の例と同等のものです。

まず、NavHostFragment を作成します。これは、実際のナビゲーション グラフを含むナビゲーション ホストとして機能します。

次に、NavHostFragment の最小限の実装を示します。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:navGraph="@navigation/nav_graph" />

</FrameLayout>

NavHostFragment には、属性 app:navGraph が含まれています。この属性を使用して、ナビゲーション グラフをナビゲーション ホストに接続します。次に、グラフの実装方法の例を示します。

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@id/profile">

    <fragment
        android:id="@+id/profile"
        android:name="com.example.ProfileFragment"
        android:label="Profile">

        <!-- Action to navigate from Profile to Friends List. -->
        <action
            android:id="@+id/action_profile_to_friendslist"
            app:destination="@id/friendslist" />
    </fragment>

    <fragment
        android:id="@+id/friendslist"
        android:name="com.example.FriendsListFragment"
        android:label="Friends List" />

    <!-- Add other fragment destinations similarly. -->
</navigation>

アクションを使用して、個別のデスティネーション間の接続を定義します。この例では、friendslist に移動するアクションが profile フラグメントに含まれています。詳細については、ナビゲーション アクションとフラグメントを使用するをご覧ください。

編集者

アプリのナビゲーション グラフは、Android Studio の Navigation Editor を使用して管理できます。前のセクションでも使用したこのエディタは、NavigationFragment の XML の作成と編集に使用できる GUI です。

詳しくは、Navigation Editor をご覧ください。

ネストされたグラフ

ネストされたグラフを使用することもできます。その場合は、グラフをナビゲーション デスティネーションとして使用します。詳しくは、ネストされたグラフをご覧ください。

関連情報

ナビゲーションの基本的な概念については、以下のガイドをご覧ください。

  • 概要: Navigation コンポーネントの概要について説明しています。この内容をしっかり理解してください。
  • アクティビティのデスティネーション: ユーザーをアクティビティに誘導するデスティネーションの実装方法を、例に沿って説明しています。
  • ダイアログのデスティネーション: ユーザーをダイアログに誘導するデスティネーションの作成方法を、例に沿って説明しています。
  • デスティネーションに移動する: デスティネーション間を移動する方法について詳しく説明しています。
  • ネストグラフ: あるナビゲーション グラフを別のナビゲーション グラフ内にネストする方法に関する詳細なガイド。