6 月 3 日の「#Android11: The Beta Launch Show」にぜひご参加ください。

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

Navigation コンポーネントは、ナビゲーション グラフを使用してアプリのナビゲーションを管理します。ナビゲーション グラフは、ユーザーがデスティネーション間を移動する際に通過可能な論理接続(アクション)を含め、アプリ内のすべてのデスティネーションを格納するリソース ファイルです。Android Studio の Navigation Editor を使用すると、アプリのナビゲーション グラフを管理できます。

このトピックでは、アプリのナビゲーション グラフを設計する際のベスト プラクティスと推奨事項について説明します。

トップレベル ナビゲーション グラフ

アプリのトップレベル ナビゲーション グラフは、ユーザーがアプリを起動したときに最初に表示される開始デスティネーションから始まり、ユーザーがアプリ内を移動する際に表示されるすべてのデスティネーションを含める必要があります。

図 1: トップレベル ナビゲーション グラフ

ネストグラフ

アプリ内のログインフローやウィザード、各種サブフローは通常、ネスト ナビゲーション グラフとして表現するのが最適です。このように自己完結型のサブナビゲーション フローをネストすることにより、アプリの UI のメインフローの把握と管理が容易になります。また、ネストグラフは再利用可能です。さらに、ある種のカプセル化も実現できます。ネストグラフの外部にあるデスティネーションが、ネストグラフの内部にあるデスティネーションに直接アクセスすることはできません。代わりに、navigate() を使用してネストグラフ自体にアクセスする必要があります。内部ロジックは、グラフの残りの部分に影響を与えることなく変更できます。

図 1 のトップレベル ナビゲーション グラフを例として、ユーザーがアプリを初めて起動したときに限り、title_screen 画面と register 画面を表示するように設定するとします。そして、ユーザー情報を保存し、次回以降のアプリの起動時には、直接 match 画面にユーザーを誘導するとします。ベスト プラクティスとしては、match 画面をトップレベル ナビゲーション グラフの開始デスティネーションに設定して、title_screen 画面と register 画面はネストグラフに移動します。図 2 をご覧ください。

図 2: ネストグラフが追加されたトップレベル ナビゲーション グラフ

match 画面が起動したら、登録済みユーザーがいるかチェックします。ユーザーが登録されていない場合は、register 画面に移動します。条件付きナビゲーション シナリオの詳細については、条件付きナビゲーションをご覧ください。

グラフ構造をモジュール化するもう 1 つの方法として、親ナビゲーション グラフ内の <include> 要素を通じて、別のグラフを子グラフとして組み込む方法があります。この方法の場合、組み込まれる側のグラフを、独立したモジュールやプロジェクト内で定義できるため、最大限の再利用性を実現できます。

アプリが、ナビゲーション グラフを格納しているライブラリ モジュールに依存している場合は、<include> 要素を使用することで、このナビゲーション グラフを参照できます。

上記のトリビアゲームの例を基に、アプリのゲーム中核部分を個別のライブラリ モジュールに分離するとします。これにより、さまざまなアプリに in_game 画面、results_winner 画面、game_over 画面を組み込むことができるようになります。以下の例をご覧ください。

<!-- App Module Navigation Graph -->
    <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"
               app:startDestination="@id/match">

       <fragment android:id="@+id/match"
               android:name="com.example.android.navigationsample.Match"
               android:label="fragment_match">

           <!-- Launch into In Game Modules Navigation Graph -->
           <action android:id="@+id/action_match_to_in_game_nav_graph"
               app:destination="@id/in_game_nav_graph" />
       </fragment>

       <include app:graph="@navigation/in_game_navigation" />

    </navigation>
    
<!-- Game Module Navigation Graph -->
    <?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"
       android:id="@+id/in_game_nav_graph"
       app:startDestination="@id/in_game">

       <fragment
           android:id="@+id/in_game"
           android:name="com.example.android.gamemodule.InGame"
           android:label="Game">
           <action
               android:id="@+id/action_in_game_to_resultsWinner"
               app:destination="@id/results_winner"  />
           <action
               android:id="@+id/action_in_game_to_gameOver"
               app:destination="@id/game_over"  />
       </fragment>

       <fragment
           android:id="@+id/results_winner"
           android:name="com.example.android.gamemodule.ResultsWinner" >

           <!-- Action back to destination which launched into this in_game_nav_graph-->
           <action android:id="@+id/action_pop_out_of_game"
                               app:popUpTo="@id/in_game_nav_graph" />

       </fragment>

       <fragment
           android:id="@+id/game_over"
           android:name="com.example.android.gamemodule.GameOver"
           android:label="fragment_game_over"
           tools:layout="@layout/fragment_game_over" >

          <!-- Action back to destination which launched into this in_game_nav_graph-->
           <action android:id="@+id/action_pop_out_of_game"
                               app:popUpTo="@id/in_game_nav_graph" />

     </fragment>

    </navigation>
    

トップレベル グラフをメインアプリ モジュール内に格納し、参照する必要のあるナビゲーションを格納するモジュールに対して Gradle 参照を設定するようにしてください。

グローバル アクション

アプリのデスティネーションに到達できるパスが複数ある場合、そのデスティネーションに移動するためのグローバル アクションを定義する必要があります。グローバル アクションを利用すると、どこからでも対象のデスティネーションに移動できるようになります。

グローバル アクションを上記のライブラリ モジュール サンプルに適用してみましょう。このサンプルでは、results_winner デスティネーションと game_over デスティネーションの両方に対して同じアクションが定義されています。この共通アクションを単一のグローバル アクションとして抽出し、両方のデスティネーションから参照するようにします。以下の例をご覧ください。

<?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"
       android:id="@+id/in_game_nav_graph"
       app:startDestination="@id/in_game">

       <!-- Action back to destination which launched into this in_game_nav_graph-->
       <action android:id="@+id/action_pop_out_of_game"
                           app:popUpTo="@id/in_game_nav_graph"
                           app:popUpToInclusive="true"  />

       <fragment
           android:id="@+id/in_game"
           android:name="com.example.android.gamemodule.InGame"
           android:label="Game">
           <action
               android:id="@+id/action_in_game_to_resultsWinner"
               app:destination="@id/results_winner"  />
           <action
               android:id="@+id/action_in_game_to_gameOver"
               app:destination="@id/game_over"  />
       </fragment>

       <fragment
           android:id="@+id/results_winner"
           android:name="com.example.android.gamemodule.ResultsWinner" />

       <fragment
           android:id="@+id/game_over"
           android:name="com.example.android.gamemodule.GameOver"
           android:label="fragment_game_over"
           tools:layout="@layout/fragment_game_over" />

    </navigation>
    

フラグメント内でグローバル アクションを使用する方法の詳細と例については、グローバル アクションをご覧ください。