ナビゲーションコンポーネントのメリット
本連載は、Android Jetpackを紹介しています。今回は、Androidアプリの画面遷移をまとめて管理できる便利なライブラリとして、ナビゲーションコンポーネントを紹介します。
なお、今回のサンプルデータは、GitHubから参照できます。
フラグメントを利用した通常の画面遷移の問題点
ナビゲーションを紹介するためのサンプルとして、メモ帳アプリのようなものを考えます。アプリを起動すると、図1のメモ内容のタイトルのみをリスト表示した画面が表示されます。
その後、このリストをタップすると、図2のメモ内容の詳細画面が表示されます。もちろん、メモ詳細画面の左上のナビゲーションアイコンをクリックすると、元のメモリスト画面に戻ります。
このような画面遷移を実現する方法として、アクティビティはMainActivityのみとして、そのアクティビティに乗るフラグメントを置き換える画面遷移の方法、つまり、シングルアクティビティマルチフラグメントアーキテクチャを採用して、メモリスト画面をMemoListFragment、メモ詳細画面をMemoDetailFragmentとしたとします。
すると、通常の画面遷移コードは、MemoListFragment内のリストをタップした際のリスナ内に、リスト1の通り表示フラグメントをMemoDetailFragmentに置き換える処理を記述します。
// フラグメントマネージャの取得。
FragmentManager manager = getParentFragmentManager();
// フラグメントトランザクションの開始。
FragmentTransaction transaction = manager.beginTransaction();
// バックスタックが正しく動作するように設定。
transaction.setReorderingAllowed(true);
// バックスタックへの追加。
transaction.addToBackStack("MemoList");
// フラグメントコンテナ上のフラグメントを詳細画面に置き換える。
transaction.replace(R.id.fragmentContainer, MemoDetailFragment.class, arguments);
// フラグメントトランザクションのコミット。
transaction.commit();
一方、MemoDetailFragmentのナビゲーションアイコンクリック時の処理では、リスト2のように、バックスタックからポップさせるコードを記述します。
// フラグメントマネージャの取得。 FragmentManager manager = getParentFragmentManager(); // バックスタックからポップ。 manager.popBackStack();
こういった画面遷移コードには、以下の問題があります。
画面遷移の全体像がわからない。
例えば、MemoListFragmentからどの画面に遷移しているのかは、MemoListFragment内の該当コードを探し出して読まないとわかりません。もちろん、これは専用のドキュメントを作成すればよいわけですが、アプリ内のファイルにはまとまった情報はありません。
バックスタックへの登録を忘れると戻れない。
前の画面や、場合によってはさらにその前の画面に戻るという処理は、バックスタックからのポップとなるため、画面遷移時にバックスタックへの登録処理コードを記述し忘れると戻る処理が成立しなくなります。これは、すなわち、バックボタン(バックジェスチャー)の挙動そのものも変わってきます。
ナビゲーションコンポーネントとは
これら問題を一挙に解決してくれるのがナビゲーションコンポーネントです。ナビゲーションコンポーネントの仕様に従って、アプリ内に画面遷移をひとつのファイルに定義すると、そのファイルを参照することで、アプリ内のすべて画面遷移をまとめて参照できます。このファイルに記述されたデータ構造のことを、ナビゲーショングラフといいます。
しかも、Android Studioには、このナビゲーショングラフをビジュアルに表示し、GUIで編集できる機能が備わっています。この機能をナビゲーションエディタと言います。
図3は、メモリスト画面からメモ詳細画面の画面遷移を定義したナビゲーショングラフを表示させたナビゲーションエディタ画面です。今回はサンプルとして2画面のみのアプリですが、複数画面で複雑な遷移を行うものでも、このようにビジュアルに表示されます。
ナビゲーションコンポーネントの利用準備
概論はここまでにして、実際にナビゲーションコンポーネントの利用方法を解説していきます。まずは、その準備からです。
依存ライブラリの追加
ナビゲーションコンポーネントを利用する場合、依存ライブラリを追加する必要があります。これは、build.gradle.kts(:app)ファイルのdependenciesブロックへの、リスト3のコードの追記です。
なお、バージョン番号は、原稿執筆時点での番号です。最新バージョンに関しては、こちらのページを参照してください。
implementation("androidx.navigation:navigation-fragment:2.9.5")
implementation("androidx.navigation:navigation-ui:2.9.5")
フラグメントコンテナへの属性記述
次に、activity_main.xml中に記述されているフラグメントコンテナを、ナビゲーションコンポーネントを利用するものとして定義します。これは、リスト4のコードです。
<androidx.constraintlayout.widget.ConstraintLayout
:
<androidx.fragment.app.FragmentContainerView
android:id="@+id/navHostFragmentContainer"
android:name="androidx.navigation.fragment.NavHostFragment" // (1)
app:navGraph="@navigation/nav_graph" // (2)
app:defaultNavHost="true" // (3)
: />
</androidx.constraintlayout.widget.ConstraintLayout>
リスト4のフラグメントコンテナ定義のポイントは(1)です。コンテナに埋め込むフラグメントとして、すなわち、name属性の値として、自作したものではなく、NavHostFragmentとします。これにより、ナビゲーションコンポーネントがナビゲーショングラフに従って自動的にフラグメントコンテナに適切なフラグメントを埋め込みます。
そのナビゲーショングラフを指定する属性が、(2)のnavGraphです。こちらの作り方は、次節で紹介します。
なお、(3)のdefaultNavHostの属性値としてtrueを指定すると、バックボタン(バックジェスチャー)の処理をバックスタックからのポップに置き換えてくれます。これにより、バックボタン(バックジェスチャー)でアプリが終了しなくなります。
