Navigation アーキテクチャ コンポーネントは、ナビゲーションの実装を簡素化するとともに、アプリのナビゲーション フローを視覚化するためにも役立ちます。このライブラリには、次のような多くの利点があります。
- フラグメント トランザクションの自動処理
- 上へと戻るがデフォルトで正しく処理される
- アニメーションと遷移のデフォルトの動作
- 高度なオペレーションとしてのディープリンク
- ナビゲーション UI パターン(ナビゲーション ドロワーやボトム ナビゲーションなど)をわずかな追加作業で実装できる
- ナビゲーション中のタイプセーフな情報の受け渡し
- アプリのナビゲーション フローを視覚化して編集できる Android Studio ツール
演習内容
この Codelab では、下記のサンプルアプリを使用します。
すべてのアクティビティとフラグメントはすでに作成済みです。Navigation コンポーネントを使用してそれらを接続し、その過程で以下を実装します。
- 視覚的なナビゲーション グラフ
- デスティネーションとアクションによるナビゲーション
- 遷移アニメーション
- メニュー ナビゲーション、ボトム ナビゲーション、メニュー ドロワー ナビゲーション
- タイプセーフな引数の受け渡し
- ディープリンク
前提条件
- Kotlin の基礎知識(この Codelab は Kotlin を使用します)
- Android Studio 3.2 以上
- API レベル 14 以上を実行するエミュレータまたはデバイス
コードを入手する
GitHub から、ナビゲーション Codelab のクローンを作成します。
$ git clone https://github.com/googlecodelabs/android-navigation
または、リポジトリを ZIP ファイルとしてダウンロードすることもできます。
Android Studio 3.3 以上を入手する
Android Studio 3.3 以上を使用していることを確認します。Android Studio ナビゲーション ツールには、このバージョンが必要です。
Android Studio の最新バージョンをダウンロードする必要がある場合は、こちらからダウンロードできます。
ナビゲーションの概要
Navigation コンポーネントは、協調して機能する 3 つの主要部分で構成されています。それらを次に示します。
- ナビゲーション グラフ(新しい XML リソース)- すべてのナビゲーション関連情報を 1 つの場所に集めたリソース。アプリ内のすべての場所(「デスティネーション」と呼びます)と、ユーザーがアプリ内でたどる可能性があるパスが含まれています。
- NavHostFragment(レイアウト XML ビュー)- レイアウトに追加する特別なウィジェット。ナビゲーション グラフのさまざまなデスティネーションを表示します。
- NavController(Kotlin / Java オブジェクト)- ナビゲーション グラフ内の現在の位置をトラッキングするオブジェクト。ユーザーがナビゲーション グラフ内を移動する際の、
NavHostFragment
内のデスティネーション コンテンツの差し替え(スワップ)を管理します。
ナビゲーション中は、NavController オブジェクトを使用して、移動先またはナビゲーション グラフ内でたどるパスを指定します。そうすると、NavController
が NavHostFragment 内の適切なデスティネーションを表示します。
以上が基本的なコンセプトです。ナビゲーションがどのように行われるかを実際に見てみましょう。最初は、新しいリソースであるナビゲーション グラフです。
デスティネーション
Navigation コンポーネントでは、デスティネーションというコンセプトを導入しています。デスティネーションはアプリ内で移動先として使用できる場所であり、通常はフラグメントまたはアクティビティです。それらはデフォルトでサポートされていますが、必要であれば独自のデスティネーション タイプを作成することもできます。
ナビゲーション グラフ
ナビゲーション グラフは、ユーザーがアプリでたどる可能性があるすべてのパスを定義する新しいリソースタイプです。特定のデスティネーションから到達できるすべてのデスティネーションを視覚的に表示します。Android Studio の Navigation Editor にグラフが表示されます。サンプルアプリで作成する開始ナビゲーション グラフの一部を次に示します。
Navigation Editor の操作
1. res/navigation/mobile_navigation.xml
を開きます。
2. [Design] をクリックして、デザインモードに切り替えます。
次のものが表示されます。
使用可能なデスティネーションがナビゲーション グラフに表示されます。デスティネーション間の矢印を「アクション」と呼びます。アクションについては後で詳しく説明します。
3. デスティネーションをクリックして、その属性を表示します。
4. アクション(矢印)をクリックして、その属性を表示します。
ナビゲーション XML ファイルの構造
Layout Editor でレイアウト XML を変更する場合と同様に、グラフィカルな Navigation Editor で行ったすべての変更は、基になっている XML ファイルに反映されます。
[Text] タブをクリックします。
次のような XML が表示されます。
<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/home_dest">
<!-- ...tags for fragments and activities here -->
</navigation>
次の点に注目してください。
<navigation>
は、各ナビゲーション グラフのルートノードです。<navigation>
には 1 つ以上のデスティネーションが含まれ、それぞれが<activity>
要素または<fragment>
要素で表されます。app:startDestination
は、ユーザーが初めてアプリを開いたときにデフォルトで起動されるデスティネーションを指定する属性です。
フラグメント デスティネーションを見てみましょう。
<fragment
android:id="@+id/flow_step_one_dest"
android:name="com.example.android.codelabs.navigation.FlowStepFragment"
tools:layout="@layout/flow_step_one_fragment">
<argument
.../>
<action
android:id="@+id/next_action"
app:destination="@+id/flow_step_two_dest">
</action>
</fragment>
次の点に注目してください。
android:id
は、この XML とコードの別の場所でデスティネーションを参照するために使用できるフラグメントの ID を定義しています。android:name
は、デスティネーションに移動したときにインスタンス化するフラグメントの完全修飾クラス名を宣言しています。tools:layout
は、グラフィカル エディタに表示するレイアウトを指定しています。
一部の <fragment>
タグには、<action>
、<argument>,
、<deepLink>
も含まれています。これらについては後で説明します。
サンプルアプリでは、少数のデスティネーションが最初からグラフに含まれています。このステップで、新しいデスティネーションを追加します。デスティネーションにナビゲートするには、ナビゲーション グラフにデスティネーションを追加する必要があります。
1. res/navigation/mobile_navigation.xml
を開いて、[Design] タブをクリックします。
2. 新規デスティネーション アイコンをクリックし、「settings_fragment」を選択します。
新しいデスティネーションが作成され、フラグメントのレイアウトのプレビューがデザインビューにレンダリングされます。
なお、XML ファイルを直接編集してデスティネーションを追加することもできます。
mobile_navigation.xml
<fragment
android:id="@+id/settings_dest"
android:name="com.example.android.codelabs.navigation.SettingsFragment"
android:label="@string/settings"
tools:layout="@layout/settings_fragment" />
これで便利なナビゲーション グラフが作成されましたが、まだ実際にそれを使ってナビゲーションを行ったわけではありません。
アクティビティとナビゲーション
Navigation コンポーネントは、ナビゲーションの原則で概説されているガイダンスに従います。ナビゲーションの原則では、アプリのエントリ ポイントとしてアクティビティを使用することが推奨されています。アクティビティには、ボトム ナビゲーションなどのグローバル ナビゲーションも含まれます。
これに対して、フラグメントはデスティネーション固有の実際のレイアウトになります。
これらすべてを機能させるには、アクティビティ レイアウトを変更して、NavHostFragment
という特別なウィジェットを含める必要があります。NavHostFragment
は、ナビゲーション グラフ内をナビゲートする際に、さまざまなフラグメント デスティネーションを差し替えます。
上記の画像と似たナビゲーションをサポートする単純なレイアウトは、次のようになります。このコードの例は res/layout-470dp/navigation_activity.xml
にあります。
<LinearLayout
.../>
<androidx.appcompat.widget.Toolbar
.../>
<fragment
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/mobile_navigation"
app:defaultNavHost="true"
/>
<com.google.android.material.bottomnavigation.BottomNavigationView
.../>
</LinearLayout>
次の点に注目してください。
- これはアクティビティ用のレイアウトです。ボトム ナビゲーションとツールバーを含むグローバル ナビゲーションがあります。
android:name="androidx.navigation.fragment.NavHostFragment"
とapp:defaultNavHost="true"
により、システムの [戻る] ボタンがNavHostFragment
に接続されます。app:navGraph="@navigation/mobile_navigation"
は、NavHostFragment
をナビゲーション グラフに関連付けます。このナビゲーション グラフは、ユーザーが移動できるすべてのデスティネーションを、このNavHostFragment
で指定しています。
NavController
最後に、ユーザーがボタンクリックなどの操作を行ったときに、ナビゲーション コマンドをトリガーする必要があります。NavController
という特別なクラスは、NavHostFragment
内のフラグメントのスワップをトリガーします。
// Command to navigate to flow_step_one_dest
findNavController().navigate(R.id.flow_step_one_dest)
ナビゲートするには、デスティネーション ID またはアクション ID のいずれかを渡します。それらはナビゲーション グラフ XML で定義されている ID です。サンプルでは、デスティネーション ID を渡しています。
navigate()
や popBackStack(),
などのメソッドを呼び出すとき、NavController
は移動先または移動元のデスティネーションのタイプに基づいてそれらのコマンドを適切なフレームワーク オペレーションに変換するので、大変便利です。たとえば、アクティビティ デスティネーションで navigate()
を呼び出すと、NavController
が startActivity()
を呼び出してくれます。
サンプルの NavHostFragment
に関連付けられた NavController
オブジェクトを取得する方法はいくつかあります。Kotlin では、フラグメント内、アクティビティ内、ビュー内のどこからナビゲーション コマンドを呼び出すかに応じて、次のいずれかの拡張関数を使用することをおすすめします。
NavController を使用してデスティネーションに移動する
それでは、NavController
を使用してナビゲーションを行ってください。[Navigate to Destination] ボタンを連結して、flow_step_one_dest
デスティネーション(つまり、FlowStepFragment
であるデスティネーション)に移動します。
1. HomeFragment.kt
を開きます。
2. onViewCreated()
で navigate_destination_button
を連結します。
HomeFragment.kt
val button = view.findViewById<Button>(R.id.navigate_destination_button)
button?.setOnClickListener {
findNavController().navigate(R.id.flow_step_one_dest, null)
}
3. アプリを実行して、[Navigate To Destination] ボタンをクリックします。このボタンは flow_step_one_dest
デスティネーションにナビゲートします。
クリック リスナーのコードは次のようになります。
val button = view.findViewById<Button>(R.id.navigate_destination_button)
button?.setOnClickListener(
Navigation.createNavigateOnClickListener(R.id.flow_step_one_dest, null)
)
次に示すように、個々の navigate()
呼び出しには、単純なデフォルトの遷移が関連付けられています。
デフォルトの遷移と、呼び出しに関連付けられたその他の属性は、NavOptions
のセットを含めることによりオーバーライドできます。NavOptions
は、必要なオプションのみをオーバーライドして設定できる Builder
パターンを使用します。NavOptions 用の ktx DSL もあります。サンプルではこれを使用しています。
アニメーション化された遷移の場合は、anim
リソース フォルダで XML アニメーション リソースを定義して、それらのアニメーションを遷移に使用できます。アプリコードには、次のようなリソースが含まれています。
カスタム遷移を追加する
コードを更新して、[Navigate To Destination] ボタンを押すとカスタム遷移アニメーションが表示されるようにします。
1. HomeFragment.kt
を開きます。
2. NavOptions
を定義して、navigate_destination_button
に対する navigate()
呼び出しに渡します。
val options = navOptions {
anim {
enter = R.anim.slide_in_right
exit = R.anim.slide_out_left
popEnter = R.anim.slide_in_left
popExit = R.anim.slide_out_right
}
}
view.findViewById<Button>(R.id.navigate_destination_button)?.setOnClickListener {
findNavController().navigate(R.id.flow_step_one_dest, null, options)
}
3. ステップ 5 で追加したコードがまだ残っている場合は、削除します。
4. [Navigate To Destination] ボタンをタップするとフラグメントが別の画面にスライドし、[戻る] ボタンを押すと元の画面にスライドすることを確認します。
アクション
ナビゲーション システムでは、「アクション」を使用したナビゲーションも可能です。前述のように、ナビゲーション グラフに表示される線はアクションを視覚的に表したものです。
デスティネーションによるナビゲーションと比べて、アクションによるナビゲーションには次のような利点があります。
- アプリ全体のナビゲーション パスを視覚化できる
- 遷移アニメーション、引数の値、バックスタックの動作など、設定可能な関連属性をアクションに追加できる
- プラグインの Safe Args をナビゲーションに使用できる(この後すぐ説明します)
flow_step_one_dest
と flow_step_two_dest
を接続するアクションの画像と XML を次に示します。
<fragment
android:id="@+id/flow_step_one_dest"
android:name="com.example.android.codelabs.navigation.FlowStepFragment">
<argument
.../>
<action
android:id="@+id/next_action"
app:destination="@+id/flow_step_two_dest">
</action>
</fragment>
<fragment
android:id="@+id/flow_step_two_dest"
android:name="com.example.android.codelabs.navigation.FlowStepFragment">
<!-- ...removed for simplicity-->
</fragment>
次の点に注目してください。
- アクションはデスティネーション内でネストされます。これはナビゲーションの開始デスティネーションです。
- アクションには、flow_step_two_dest を参照するデスティネーション引数が含まれています。これは移動先の ID です。
- アクションの ID は「next_action」です。
次の例は、flow_step_two_dest
を home_dest
に接続するもう 1 つのアクションを示しています。
<fragment
android:id="@+id/home_dest"
android:name="com.example.android.codelabs.navigation.HomeFragment"
.../>
<fragment
android:id="@+id/flow_step_two_dest"
android:name="com.example.android.codelabs.navigation.FlowStepFragment">
<argument
.../>
<action
android:id="@+id/next_action"
app:popUpTo="@id/home_dest">
</action>
</fragment>
次の点に注目してください。
- next_action という同じ ID が、
flow_step_two_dest
をhome_dest
に接続するアクションに使用されています。next_action という ID を使用して、flow_step_one_dest
またはflow_step_two_dest
からナビゲートできます。これは、アクションによって抽象化のレベルを提供し、コンテキストに応じて異なる場所にナビゲートする方法の一例です。 popUpTo
属性が使用されています。このアクションは、ユーザーがhome_dest
に到達するまで、バックスタックからフラグメントをポップオフします。
アクションを使用してナビゲートする
それでは、[Navigate with Action] ボタンを連結して、その名にふさわしい動作を設定しましょう。
1. mobile_navigation.xml
ファイルをデザインモードで開きます。
2. home_dest
から flow_step_one_dest
に矢印をドラッグします。
3. アクションの矢印(青色で表示)を選択し、アクションのプロパティを次のように変更します。
- [ID] = next_action
- [Transition] の [Enter] = slide_in_right
- [Transition] の [Exit] = slide_out_left
- [Transition] の [Pop Enter] = slide_in_left
- [Transition] の [Pop Exit] = slide_out_right
4. [Text] タブをクリックします。
次のように、home_dest
デスティネーションの下に next_action
アクションが新しく追加されていることを確認します。
mobile_navigation.xml
<fragment android:id="@+id/home_dest"
...>
<action android:id="@+id/next_action"
app:destination="@+id/flow_step_one"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
5. HomeFragment.kt
を開きます。
6. navigate_action_button
にクリック リスナーを追加します。
HomeFragment.kt
view.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener(
Navigation.createNavigateOnClickListener(R.id.next_action, null)
)
7. [Navigate To Action] をタップすると次の画面に移動することを確認します。
Safe Args
Navigation コンポーネントには、Safe Args という Gradle プラグインが含まれています。Safe Args は、デスティネーションとアクション用に指定された引数に対してタイプセーフなアクセスを行うためのシンプルなオブジェクトとビルダークラスを生成します。
Safe Args を使用すると、デスティネーション間で値を渡すときに次のようなコードを使わずに済みます。
val username = arguments?.getString("usernameKey")
このコードは、セッターとゲッターをすでに生成している次のコードに置き換えることができます。
val username = args.username
Safe Args を使用して値を渡す
1. プロジェクトの build.gradle
ファイルを開いて、Safe Args プラグインがあることを確認します。
build.gradle
dependencies {
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
//...
}
2. app/build.gradle
ファイルを開いて、適用されているプラグインを確認します。
app/build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'androidx.navigation.safeargs.kotlin'
android {
//...
}
3. mobile_navigation.xml,
を開いて、flow_step_one_dest
デスティネーションで引数がどのように定義されているかを確認します。
mobile_navigation.xml
<fragment
android:id="@+id/flow_step_one_dest"
android:name="com.example.android.codelabs.navigation.FlowStepFragment"
tools:layout="@layout/flow_step_one_fragment">
<argument
android:name="flowStepNumber"
app:argType="integer"
android:defaultValue="1"/>
<action...>
</action>
</fragment>
safeargs は、<argument>
タグを使用して FlowStepFragmentArgs
というクラスを生成します。
XML には android:name="flowStepNumber"
で指定された flowStepNumber
という引数が含まれているため、生成されるクラス FlowStepFragmentArgs
にはゲッターとセッターを持つ変数 flowStepNumber
が含まれています。
4. FlowStepFragment.kt
を開きます。
5. 下記のコード行をコメントアウトします。
FlowStepFragment.kt
// Comment out this line
// val flowStepNumber = arguments?.getInt("flowStepNumber")
この古いスタイルのコードはタイプセーフではありません。Safe Args を使用する方が適切です。
6. FlowStepFragment
を更新し、コードで生成されたクラス FlowStepFragmentArgs
を使用します。これにより、FlowStepFragment
引数がタイプセーフな方法で取得されます。
FlowStepFragment.kt
val safeArgs: FlowStepFragmentArgs by navArgs()
val flowStepNumber = safeArgs.flowStepNumber
Safe Args の Directions クラス
また、Safe Args を使用すると、引数を追加してもしなくても、タイプセーフな方法でナビゲートできます。そのためには、生成された Directions クラスを使用します。
Directions クラスは、アクションが存在するデスティネーションごとに生成されます。Directions クラスには、デスティネーションに存在する各アクションのメソッドが含まれています。
たとえば、HomeFragment.kt 内の navigate_action_button
クリック リスナーは、次のように変更できます。
HomeFragment.kt
// Note the usage of curly braces since we are defining the click listener lambda
view.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener {
val flowStepNumberArg = 1
val action = HomeFragmentDirections.nextAction(flowStepNumberArg)
findNavController().navigate(action)
}
NavigationUI と navigation-ui-ktx
Navigation コンポーネントには、NavigationUI
クラスと navigation-ui-ktx
Kotlin 拡張機能が含まれています。NavigationUI
には、メニュー項目をナビゲーション デスティネーションに関連付ける静的メソッドがあります。navigation-ui-ktx
は、同じ処理を行う拡張関数のセットです。NavigationUI
は、現在のグラフでデスティネーションと同じ ID を持つメニュー項目を検出すると、そのデスティネーションに移動するメニュー項目を構成します。
オプション メニューで NavigationUI を使用する
NavigationUI の最も簡単な使用方法の 1 つは、オプション メニューのセットアップを簡素化することです。NavigationUI は、特に onOptionsItemSelected
コールバックの処理を簡素化します。
1. MainActivity.kt を開きます。
onCreateOptionsMenu
内のメニュー overflow_menu
をインフレートするコードがすでに存在することを確認します。
2. res/menu/overflow_menu.xml
を開きます。
3. オーバーフロー メニューを更新して、settings_dest
を含めます。
overflow_menu.xml
<item
android:id="@+id/settings_dest"
android:icon="@drawable/ic_settings"
android:menuCategory="secondary"
android:title="@string/settings" />
4. MainActivity.kt
を開きます。
5. NavigationUI が onNavDestinationSelected
ヘルパー メソッドで onOptionsItemSelected
を処理するようにします。メニュー項目の目的がナビゲーションでない場合は、super.onOptionsItemSelected
で処理します。
MainActivity.kt
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment))
|| super.onOptionsItemSelected(item)
}
6. アプリを実行します。SettingsFragment に移動する ActionBar メニューが機能することを確認します。
NavigationUI を使用してボトム ナビゲーションを構成する
コードにはボトム ナビゲーションを実装するための XML レイアウト コードがすでに含まれているため、ボトム ナビゲーション バーが表示されます。しかし、それはユーザーをどこにもナビゲートしません。
1. res/layout/navigation_activity/navigation_activity.xml (h470dp)
を開いて、[Text] タブをクリックします。
ボトム ナビゲーション用の XML レイアウト コードがあり、bottom_nav_menu.xml
を参照していることを確認します。
navigation_activity.xml(h470dp)
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/bottom_nav_menu" />
2. res/menu/bottom_nav_menu.xml
を開きます。
ボトム ナビゲーションに 2 つのアイテムがあり、ナビゲーション グラフのデスティネーションとそれらの id が一致していることを確認します。
bottom_nav_menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@id/home_dest"
android:icon="@drawable/ic_home"
android:title="@string/home" />
<item
android:id="@id/deeplink_dest"
android:icon="@drawable/ic_android"
android:title="@string/deeplink" />
</menu>
ボトム ナビゲーションが NavigationUI を使用して実際に機能するようにしましょう。
3. MainActivity.kt
を開きます。
4. setupWithNavController(bottomNavigationView: BottomNavigationView, navController: NavController)
を使用して setupBottomNavMenu
メソッドを実装します。
MainActivity.kt
private fun setupBottomNavMenu(navController: NavController) {
val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
bottomNav?.setupWithNavController(navController)
}
これで、ボトム ナビゲーションが機能するようになります。
NavigationUI を使用してナビゲーション ドロワーを構成する
最後に、NavigationUI
を使用してサイド ナビゲーションとナビゲーション ドロワーを構成しましょう。これには、ActionBar の処理や、適切な「上へ」ナビゲーションの処理があります。これは、画面が十分に大きい場合や、ボトム ナビゲーションを使用するには画面の縦幅が短すぎる場合に使用されます。
まず、適切なレイアウト XML コードがすでにアプリに存在することを確認します。
1. navigation_activity.xml
と navigation_activity.xml (w960dp)
の両方を開きます。
両方のレイアウトに、nav_drawer_menu に接続された NavigationView が含まれていることを確認します。タブレット版(w960dp)では、NavigationView は常に画面に表示されます。小型のデバイスでは、NavigationView は DrawerLayout 内にネストされます。
次に、NavigationView
ナビゲーションの実装を開始します。
2. MainActivity.kt
を開きます。
3. setupWithNavController(navigationView: NavigationView, navController: NavController)
を使用して setupNavigationMenu
メソッドを実装します。このバージョンのメソッドは、BottomNavigationView
ではなく NavigationView
を受け取ることに注意してください。
MainActivity.kt
private fun setupNavigationMenu(navController: NavController) {
val sideNavView = findViewById<NavigationView>(R.id.nav_view)
sideNavView?.setupWithNavController(navController)
}
これで、ナビゲーション ビューメニューが画面に表示されますが、ActionBar には影響しません。
ActionBar
を設定するには、AppBarConfiguration
のインスタンスを作成する必要があります。AppBarConfiguration
の目的は、ツールバー、折りたたみツールバー、アクションバーに必要な構成オプションを指定することです。構成オプションでは、バーがドロワー レイアウトを処理する必要があるかどうかや、どのデスティネーションをトップレベル デスティネーションと見なすかなどを指定します。
トップレベル デスティネーションは、アプリのルートレベル デスティネーションです。これらのデスティネーションはアプリバーに [上へ] ボタンを表示せず、デスティネーションがドロワー レイアウトを使用している場合はドロワー アイコンを表示します。
4. トップレベル デスティネーション ID とドロワー レイアウトのセットを渡して、AppBarConfiguration
を作成します。
MainActivity.kt
val drawerLayout : DrawerLayout? = findViewById(R.id.drawer_layout)
appBarConfiguration = AppBarConfiguration(
setOf(R.id.home_dest, R.id.deeplink_dest),
drawerLayout)
AppBarConfiguration を作成したので、NavigationUI.setupActionBarWithNavController
を呼び出すことができます。これにより、次の処理が行われます。
- デスティネーションのラベルに基づいて ActionBar にタイトルを表示する
- ユーザーがトップレベル デスティネーションにいないときは、常に [上へ] ボタンを表示する
- ユーザーがトップレベル デスティネーションにいるときは、ドロワー アイコン(ハンバーガー アイコン)を表示する
5. setupActionBarWithNavController
を実装します。
MainActivity.kt
private fun setupActionBar(navController: NavController,
appBarConfig : AppBarConfiguration) {
setupActionBarWithNavController(navController, appBarConfig)
}
また、[上へ] ボタンが押されたときに起こることを NavigationUI に処理させる必要があります。
6. onSupportNavigationUp
をオーバーライドし、同じ AppBarConfiguration
を使用して NavigationUI.navigateUp
を呼び出します。
MainActivity.kt
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration)
}
7. コードを実行します。分割画面でアプリを開くと、機能するナビゲーション ドロワーが表示されることを確認します。「上へ」アイコンとドロワー アイコンは、適切なタイミングで表示され、正常に動作するはずです。
NavigationView
への新しいデスティネーションは簡単に追加できます。「上へ」と「戻る」のナビゲーションで機能するナビゲーション ドロワーを作成した後は、新しいメニュー項目を追加するだけです。
8. menu/nav_drawer_menu.xml
を開きます。
9. settings_dest
に対応する新しいメニュー項目を追加します。
nav_drawer_menu.xml
<item
android:id="@+id/settings_dest"
android:icon="@drawable/ic_settings"
android:title="@string/settings" />
これで、ナビゲーション ドロワーに [設定] 画面がデスティネーションとして表示されます。よくできました。
ディープリンクとナビゲーション
Navigation コンポーネントは、ディープリンクもサポートしています。ディープリンクは、実際の URL リンクまたは通知のペンディング インテントから、アプリのナビゲーションの途中にジャンプする方法です。
Navigation ライブラリを使用してディープリンクを処理する利点の 1 つは、アプリ ウィジェット、通知、ウェブリンクなど、他のエントリ ポイントからの適切なバックスタックで、適切なデスティネーションからナビゲーションを開始できることです(次のステップで説明します)。
Navigation ライブラリには、ユーザーを特定のデスティネーションにナビゲートする PendingIntent
を作成するための NavDeepLinkBuilder
クラスが用意されています。
ディープリンクを追加する
NavDeepLinkBuilder
を使用して、アプリ ウィジェットをデスティネーションに連結します。
1. DeepLinkAppWidgetProvider.kt
を開きます。
2. NavDeepLinkBuilder
で作成した PendingIntent
を追加します。
DeepLinkAppWidgetProvider
val args = Bundle()
args.putString("myarg", "From Widget");
val pendingIntent = NavDeepLinkBuilder(context)
.setGraph(R.navigation.mobile_navigation)
.setDestination(R.id.deeplink_dest)
.setArguments(args)
.createPendingIntent()
remoteViews.setOnClickPendingIntent(R.id.deep_link_button, pendingIntent)
次の点に注目してください。
setGraph
には、ナビゲーション グラフが含まれています。setDestination
は、リンクの移動先を指定しています。setArguments
には、ディープリンクに渡す引数が含まれています。
3. ホーム画面にディープリンク ウィジェットを追加します。ホーム画面を長押しすると、ウィジェットを追加するオプションが表示されます。
長押しする |
下にスクロールしてウィジェットを探す |
完了すると、ディープリンク ウィジェットが表示されます。
4. ウィジェットをタップして、Android デスティネーションが正しい引数で開くことを確認します。上部に「From Widget」と表示されます。これは、DeepLinkAppWidgetProvider で渡した引数です。
5. [戻る] ボタンを押すと home_dest
デスティネーションに移動することを確認します。
ディープリンクのバックスタック
ディープリンクのバックスタックは、渡されたナビゲーション グラフによって決定されます。選択した明示的なアクティビティに親アクティビティがある場合は、それらの親アクティビティも含まれます。
バックスタックは、app:startDestination
で指定されたデスティネーションを使用して生成されます。このアプリには 1 つのアクティビティと 1 つのナビゲーション レベルしかないため、バックスタックはユーザーを home_dest
デスティネーションにナビゲートします。
もっと複雑なナビゲーションには、ネストされたナビゲーション グラフが含まれる場合があります。ネストされたグラフでは、各レベルの app:startDestination
によってバックスタックが決定されます。ディープリンクとネストされたグラフについて詳しくは、ナビゲーションの原則をご覧ください。
<deepLink> 要素
ディープリンクの最も一般的な用途の 1 つは、ウェブリンクがアプリ内のアクティビティをオープンできるようにすることです。従来は、インテント フィルタを使用して、オープンするアクティビティに URL を関連付けていました。
Navigation ライブラリを使用すると、この処理を著しく簡素化して、ナビゲーション グラフのデスティネーションに URL を直接マッピングできます。
<deepLink>
は、グラフ内のデスティネーションに追加できる要素です。各 <deepLink>
要素には、単一の必須属性 app:uri
があります。
URI を直接マッチングする機能に加えて、次の機能がサポートされています。
- スキーマのない URI は、http および https と見なされます。たとえば、
www.example.com
はhttp://www.example.com
およびhttps://www.example.com
と一致します。 {placeholder_name}
という形式のプレースホルダを使用して、1 つ以上の文字に一致させることができます。プレースホルダの文字列値は、同じ名前のキーを持つ引数バンドルで使用できます。たとえば、http://www.example.com/users/{id}
はhttp://www.example.com/users/4
と一致します。- ワイルドカード
.*
は、ゼロ個以上の文字に一致させることができます。 - NavController は自動的に ACTION_VIEW インテントを処理して、一致するディープリンクを探します。
<deepLink> を使用して URI ベースのディープリンクを追加する
このステップでは、www.example.com にディープリンクを追加します。
1. mobile_navigation.xml
を開きます。
2. deeplink_dest
デスティネーションに <deepLink>
要素を追加します。
mobile_navigation.xml
<fragment
android:id="@+id/deeplink_dest"
android:name="com.example.android.codelabs.navigation.DeepLinkFragment"
android:label="@string/deeplink"
tools:layout="@layout/deeplink_fragment">
<argument
android:name="myarg"
android:defaultValue="Android!"/>
<deepLink app:uri="www.example.com/{myarg}" />
</fragment>
3. AndroidManifest.xml
を開きます。
4. nav-graph
タグを追加します。これにより、適切なインテント フィルタが生成されます。
AndroidManifest.xml
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<nav-graph android:value="@navigation/mobile_navigation" />
</activity>
5. ディープリンクを使用してアプリを起動します。これには、次の 2 つの方法があります。
- 次のように
adb
を使用します。
adb shell am start -a android.intent.action.VIEW -d "http://www.example.com/urlTest"
- Google アプリでナビゲートします。www.example.com/urlTest を検索バーに入力すると、曖昧さ回避ウィンドウが表示されます。[Navigation codelab] を選択します。
検索バーを使用して開く (Chrome 以外) |
曖昧さ回避ダイアログ |
いずれの場合も、画面に「urlTest」というメッセージが表示されます。これは、URL からフラグメントに渡された文字列です。
Codelab アプリには、演習対象の機能がもう 1 つあります。それはショッピング カート ボタンです。
この演習は、この Codelab で学習したスキルの総まとめです。このステップにはコメントがないので、独力で以下に挑戦してください。
- 新しいフラグメント クラスを作成する
- フラグメントをデスティネーションとしてナビゲーション グラフに追加する
- メニューを処理する NavigationUI を使用して、ショッピング カート アイコンが新しいフラグメント クラスを開くようにする
以上で、Navigation コンポーネントを支える基本的なコンセプトを習得しました。この Codelab で学習した内容は次のとおりです。
- ナビゲーション グラフの構造
- NavHostFragment と NavController
- 特定のデスティネーションに移動する方法
- アクションによるナビゲーション
- デスティネーション間で引数を渡す方法(新しい safeargs プラグインの使い方を含む)
- メニュー、ボトム ナビゲーション、ナビゲーション ドロワーによるナビゲーション
- ディープリンクによるナビゲーション
このアプリを引き続き利用することも、ご自身のアプリでナビゲーションを使用することもできます。
次に示すように、試す価値があることは他にも数多くあります。
- バックスタック(またはバックスタック操作)からデスティネーションをポップする
- ネストされたナビゲーション グラフ
- 条件付きナビゲーション
- 新しいデスティネーションのサポートの追加
Navigation コンポーネントについて詳しくは、ドキュメントをご覧ください。その他のアーキテクチャ コンポーネントについてさらに学習するには、次の Codelab をお試しください。
- Room とビュー Codelab(LiveData、ViewModel、Room)
- Android WorkManager Codelab
- Android ページング Codelab
- Android ライフサイクル対応コンポーネント Codelab (LiveData と ViewModel)
- Android 永続性 Codelab (Room)