Android Navigation Componentの使い方を徹底解説!ナビゲーショングラフで画面遷移をスマートに管理する方法
アプリケーションの設計は意外と手間がかかるものです。ホワイトボード上に、あちこちから矢印が伸びる図が描かれている——そんな光景を目にしたことはないでしょうか。
最初はアクティビティ1〜2個程度のシンプルなアプリのつもりだったのに、気づけば複数の画面フロー、多数のFragment、そして多岐にわたるユーザー操作が発生している。こんなとき、ホワイトボードに描いた設計図をそのままコードとして簡単に再現できたら素敵だと思いませんか?
そこで登場するのがNavigation Component(ナビゲーション コンポーネント)です。
初めて聞く方のために説明すると、Navigation Componentは、レイアウトの代わりに配置するUIクラスではありません。イメージとしては「地図」のようなもので、大陸の代わりにFragmentが存在し、大陸から大陸へ移動するためには方向(アクション)が必要になります。このコンポーネントを使うと、Fragment同士のつながりを俯瞰的に可視化できます。本記事では、Navigation Componentの主要な要素を解説し、アプリへの組み込み方法を学んでいきます。
それでは、出航の準備はいいですか? ⛵️
基本の準備(Learning The Ropes)
Navigation Componentは、Android Studio 3.3以降で利用可能です。使用するには、プロジェクトに以下の依存関係を追加します。
android {
...
}
dependencies {
implementation 'androidx.navigation:navigation-fragment-ktx:2.0.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.0.0'
}実際に動かす題材として、次のような構造を持つアプリを想定してみましょう。
- Start Fragment(スタート画面)
- Fragment A
- Fragment B
ユーザーはStart Fragmentから、Fragment AまたはFragment Bのどちらかに遷移できる構成です。

もしNavigation Componentを使わずにこの画面遷移を実装する場合、ボタンがクリックされたときにFragmentを開く、おなじみのコードを書く必要があります。
val myFragment : MyFragment = MyFragment()
supportFragmentManager.beginTransaction().add(R.id.container, myFragment).commit()この小さなサンプルなら数行で済みますが、アプリが大規模になり、ユーザーフローが複雑になるほど、この書き方はスケールしないことは誰もが認めるところでしょう。
ナビゲーショングラフを作ろう(All Aboard)
Navigation Componentの利用を始めるには、まずナビゲーショングラフ(Navigation Graph)を作成します。このグラフが「地図」として機能し、アプリ内のユーザーフローを定義します。作成するには、resフォルダを右クリックして新しいリソースファイルを作成しましょう。ここではファイル名をuser_flow_graph.xmlとし、リソースタイプは必ず「Navigation」を選択してください。

すべての航海には母港が必要です。Navigation Componentにおける母港がNavHostです。NavHostはプレースホルダーの役割を果たし、ユーザーがUIを操作すると、表示先のデスティネーション(宛先)がここで入れ替わります。アクティビティのメインレイアウトにNavHostを追加しましょう。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="https://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/user_flow_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>ここでは、Fragmentの表示・切り替えを行うためのfragment要素を追加しました。navGraph属性に、先ほど作成したXMLファイルを紐づけている点に注目してください。
次に、開始デスティネーション(start destination)を設定する必要があります。これを設定しないとアプリがコンパイルできません。
user_flow_graph.xmlを開き、Navigation Editor上の「+」アイコンをクリックします。

ポップアップメニューでは、後から中身を埋めるプレースホルダーを作成するか、既存のFragmentから選択することができます。

ユーザーフローはStart Fragmentから始まるので、まずはそれを選択しましょう。

続けて、残りの2つのFragment AとFragment Bも追加します。

2つのデスティネーションをつなぐには、デスティネーションにカーソルを合わせたときに表示されるドットをクリックし、別のデスティネーションへドラッグします。

こうしてStart FragmentとFragment A・Bの間に作られたのが、アクション(action)です。
コードで遷移を実装する(Shiver me Timbers)
「デスティネーションをつなぐだけで、魔法のように全部動くのでは?」と思った方もいるかもしれません。
残念ながら、そうはいきません。
コード側で「指定のデスティネーションへ遷移せよ」と指示する必要があります。では、どうやって実現するのか? 少しだけ魔法(自動生成)が絡みます。
まず、Safe ArgsというGradleプラグインを追加します。これは、デスティネーション間を遷移する際の型安全性を保証してくれるプラグインです。
buildscript {
/...
}
dependencies {
...
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.0.0"
}
}さらに、アプリレベルのbuild.gradleにも以下のプラグインを追加します。
apply plugin: "androidx.navigation.safeargs.kotlin"
また、gradle.propertiesファイルにandroid.useAndroidX=trueが設定されていることも確認しておきましょう。
先へ進む前に、なぜこれらの設定が必要なのかを理解しておきましょう。基本的に、先ほどアクションを作成した際、Android Studioはバックグラウンドでアクションを呼び出すためのコードを生成しています。このコードは、各アクションを表すメソッドやクラスで構成されています。Start Fragmentを例に見てみましょう。宣言したアクションに対して生成されるコードには、StartFragmentDirectionsというクラスが含まれます。このクラスのメソッドが、先ほど作成したアクションに対応しています。つまり、2つのFragmentに対して以下のメソッドが生成されます。
- StartFragmentDirections.actionStartFragmentToFragmentA()
- StartFragmentDirections.actionStartFragmentToFragmentB()
アクションがコードとして変換されたので、実際に使ってみましょう。
val action = StartFragmentDirections.actionStartFragmentToFragmentA()
最後のステップは、NavControllerを使用することです。このオブジェクトは、NavHost内のナビゲーションを管理する役割を担っています。NavControllerには、次の3つのいずれかの方法でアクセスできます。
- Fragment.findNavController()
- View.findNavController()
- Activity.findNavController(viewId: Int)
これらを組み合わせると、次のようなコードになります。
fragmentABtn.setOnClickListener { button ->
val action = StartFragmentDirections.actionStartFragmentToFragmentA()
button.findNavController().navigate(action)
}データを受け渡そう(Trim Your Sails)
デスティネーション間でデータを受け渡したい場合はどうすればよいでしょうか? 例えば、ユーザーがあるアイテムをクリックしたとき、そのアイテムの情報を次の画面でも使いたい、といったシーンを想像してみてください。このようなケースでは、デスティネーション引数(destination arguments)を使用します。
user_flow_graph.xmlを開いてFragment Aをクリックすると、右側にFragment Aの各種属性を示すパネルが表示されます。その中に「Arguments」という項目があるはずです。

引数を追加するには、「➕」アイコンをクリックするだけです。ポップアップウィンドウが開き、そこで引数を設定できます。名前を付け、型を選択し、必要であればデフォルト値も設定できます。ここでは、Start Fragmentから渡されるメッセージとして、String型の引数をFragment Aに追加してみましょう。

Start Fragment側では、アクションを定義して生成されたメソッドを呼び出す箇所で、引数を渡します。
fragmentABtn.setOnClickListener { button ->
val action = StartFragmentDirections.actionStartFragmentToFragmentA("Hello From Start Fragment")
button.findNavController().navigate(action)
}Fragment A側でこの値にアクセスするには、次の2つの方法があります。
- Bundleから直接値を取得する方法
class FragmentA: Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val bundle = arguments
val root = inflater.inflate(R.layout.fragment_a, container, false)
val textView : TextView = root.findViewById(R.id.textView)
textView.text = bundle?.getString("message")
return root
}
}- -ktx系の依存関係を使用している場合は、navArgsを利用する方法
class FragmentA: Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val args : FragmentAArgs by navArgs()
val root = inflater.inflate(R.layout.fragment_a, container, false)
val textView : TextView = root.findViewById(R.id.textView)
textView.text = args.message
return root
}
}✋ navArgsを使用する場合は、build.gradleファイルでJava 8のサポートを有効にする必要がある点に注意してください。
本記事で紹介したコードはすべて、GitHubリポジトリから確認できます。
-
Androidのデータバインディング入門:Data Binding Libraryでレイアウトとデータを結びつける方法
データバインディングとは、アプリが扱う「データ」と、画面上の視覚的なUI要素を結びつける(バインドする)ための手法です。この仕組みを使うと、UI側の値が更新されるたびに、裏側で保持しているデータも自動的に更新されます。 決して目新しい概念ではなく、AngularJS、React、Vueなど、多くのフロントエンドフレームワークがすでにこの仕組みを設計に取り入れています。 しかし本記事で注目するのはフロントエンドフレームワークではなく、モバイル開発です。GoogleはAndroid向けにData Binding Libraryを提供しており、これはAndroid Jetpackの一部として位置づけ
-
スマホだけでWebアプリを開発する方法 – Python × Pydroid 3 Androidアプリ開発チュートリアル
こんにちは!私は18歳のバックエンド開発者で、機械学習エンジニアを目指して勉強中です。この記事では、スマートフォン1台とPythonだけを使ってWebアプリを構築する方法を解説します😁。それでは早速始めましょう!必要なものまず必要なのは、Android 6.0以降を搭載したスマートフォンだけです。「それだけ?」と思われるかもしれませんが、本当にこれだけでOKです。次に、スマホに「Pydroid 3」というモバイルアプリをインストールしましょう。ご覧のとおり、Pydroid 3はスマートフォン上でPythonコードを書けるようにしてくれるアプリです。Google Playストアからインストール