新型コロナの影響で自宅待機になってしまい、その間勉強するものとしてSunflower
リポジトリを
勧めてもらいました。
JetPackのライブラリのうち、今回はNavigation
編です
尚、引用しているソースは明記しているところ以外は、全てSunflower
のリポジトリのものです。
環境
- Android Studioは
3.3
以上が必要 - 確認時は
3.6.2
を使用
Navgation を利用するのに必要なこと
大きく分けて3つ必要です
- 準備(依存関係の記載)
- レイアウトファイルにナビゲーションを使うことを記載する
- ナビゲーション グラフのファイルを作る
準備
依存関係の記載
- 公式によると、
build.gradle
に以下の4つの依存関係を記載する必要があります。- Java language implementation
- Kotlin
- Dynamic Feature Module Support
- Testing Navigation
公式のbuild.gradle
のサンプル
dependencies {
def nav_version = "2.3.0-alpha01"
// Java language implementation
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
// Kotlin
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
// Dynamic Feature Module Support
implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"
// Testing Navigation
androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
}
gradleファイル
それでは、Sunflowerのbuild.gradle
を見てみましょう。
Navigation
と関係ないところは省略
build.gradle(sunflower)
buildscript {
// Define versions in a single place
ext {
:
navigationVersion = '2.2.0'
:
}
dependencies {
:
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
}
}
公式のチュートリアルにはないものが含まれています!
こちらは safe args
の機能を利用する際に記載の必要があるものです。
Navgation利用時に安全にパラメータを渡す機能です。
こちらはroot
のbuild.gradle
に記載する必要があるとのこと。
https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args
build.gradle(:app)
dependencies {
:
implementation "androidx.navigation:navigation-fragment-ktx:$rootProject.navigationVersion"
implementation "androidx.navigation:navigation-ui-ktx:$rootProject.navigationVersion"
:
$rootProject.navigationVersion
はbuild.gradle(sunflower)
で定義しているnavigationVersion = '2.2.0'
ですね
ここからわかること
- Sunflowerリポジトリでは、下記のものは利用していないので、依存関係の記載がない。
Java
-
Dynamic Feature Module Support
、 Navigationのテスト
-
kotlin
は使っているので、依存関係の記載がある。
layout
次にレイアウトファイルを見てみましょう。
最初に表示される画面から詳細画面に遷移するところで使っているはずなので、
最初に表示する画面を見てみます。
起動時のActivity
はGardenActivity
でそこでsetContentView
しているxmlファイルはactivity_garden.xml
です
レイアウトはこんな感じ
activity_garden.xml
<fragment
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_garden"/>
</FrameLayout>
お、やっぱりここにNavgation
関連の設定をしています。
必要なこと
-
android:name
にはandroidx.navigation.fragment.NavHostFragment
を設定する- この
fragment
はNavigation
を使うよ!
- この
-
app:navGraph
には 利用するNavigationのXMLファイルを記載します。ここでNavGraph
と関連付けます。- ナビゲーショングラフはこのファイルを使うよ!
その他
-
app:defaultNavHost="true"
を設定すると、戻るボタンで戻るようになります。- 1つの画面に複数の
Fragment
があり、両方でNavgation
を利用している時などに、戻るボタンを効かなくする、
とかの時にはfalse
にする模様
- 1つの画面に複数の
ナビゲーショングラフ
レイアウトで指定されていたnav_garden
のファイルを見てみましょう
Navigation Editor
こんな感じで、画面が複数と、その間を結ぶ矢印が表示されています。
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/view_pager_fragment">
<fragment
android:id="@+id/view_pager_fragment"
android:name="com.google.samples.apps.sunflower.HomeViewPagerFragment"
tools:layout="@layout/fragment_view_pager">
<action
android:id="@+id/action_view_pager_fragment_to_plant_detail_fragment"
app:destination="@id/plant_detail_fragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
</fragment>
<fragment
android:id="@+id/plant_detail_fragment"
android:name="com.google.samples.apps.sunflower.PlantDetailFragment"
android:label="@string/plant_details_title"
tools:layout="@layout/fragment_plant_detail">
<argument
android:name="plantId"
app:argType="string" />
</fragment>
</navigation>
ポイント
- 最初に表示する画面が
<navigation>
タグのapp:startDestination
に記載されている。 - 画面遷移に関連する画面が
<fragment>タグ
で記載されている。 - 画面遷移の処理詳細が、遷移元の画面に
<action>
タグで記載されている。 - 遷移先画面に渡されるパラメータが遷移先画面に
<argment>
タグで記載されている。
※この辺りは、Navigation Editor
で画面をクリックしてグイーンとやって・・・みたいな感じで設定できる部分もあるようです!
(iOS
でいうStoryboard
みたいな感じでしょうか?)
https://developer.android.com/guide/navigation/navigation-getting-started?hl=ja#Designate-start
actionタグ設定値の記載方法について
特有の設定値については下記となっています。
※アニメーション関連の設定値が分かりづらかったので
こちらを参照しました
https://stackoverflow.com/questions/56285197/what-is-the-difference-between-enteranim-popenteranim-exitanim-popexitanim
https://developer.android.com/guide/navigation/navigation-animate-transitions
つまるところ、アニメーションはPUSH時とPOP時で分けて記述が可能ということのようです。
名称 | 内容 | 設定されている値 |
---|---|---|
android:id |
遷移アクションの名称(これをソース内に記載する) | @id/action_view_pager_fragment_to_plant_detail_fragment |
app:destination |
遷移先の画面(宛先) | @id/plant_detail_fragment |
app:enterAnim |
宛先に遷移する際のアニメーション | @anim/slide_in_right |
app:exitAnim |
宛先からに出て行く時のアニメーション | @anim/slide_out_left |
app:popEnterAnim |
宛先にPOPで遷移する時のアニメーション | @anim/slide_in_left |
app:popExitAnim |
宛先からPOPで出て行く時のアニメーション | @anim/slide_out_right |
使用箇所について
- RecyclerViewの項目をタップした時に、
navigatioToPlant
で画面遷移を実施している。 -
navigatioToPlant
のactionViewPagerFragmentToPlantDetailFragment(plantId)
で遷移先を決定するNavDirections
を取得している。 -
NavDirections
の取得にはNavDirections
インターフェースを継承したクラスActionViewPagerFragmentToPlantDetailFragment
のコンストラクタActionViewPagerFragmentToPlantDetailFragment(plantId)
使用している。 -
ActionViewPagerFragmentToPlantDetailFragment
クラスでは、NavDirections
インターフェースのgetActionId()
メソッドをオーバーライドしている。 -
getActionId()
メソッドは action_view_pager_fragment_to_plant_detail_fragmentから取得している。
⬆︎先ほどナビゲーショングラフのXMLファイルで定義していたaction
のIDですね!(やっと出てきた!(^^;;)
class ViewHolder(
private val binding: ListItemGardenPlantingBinding
) : RecyclerView.ViewHolder(binding.root) {
init {
binding.setClickListener { view ->
binding.viewModel?.plantId?.let { plantId ->
navigateToPlant(plantId, view)
}
}
}
private fun navigateToPlant(plantId: String, view: View) {
val direction = HomeViewPagerFragmentDirections
.actionViewPagerFragmentToPlantDetailFragment(plantId)
view.findNavController().navigate(direction)
}
class HomeViewPagerFragmentDirections private constructor() {
private data class ActionViewPagerFragmentToPlantDetailFragment(
val plantId: String
) : NavDirections {
override fun getActionId(): Int = R.id.action_view_pager_fragment_to_plant_detail_fragment
override fun getArguments(): Bundle {
val result = Bundle()
result.putString("plantId", this.plantId)
return result
}
}
companion object {
fun actionViewPagerFragmentToPlantDetailFragment(plantId: String): NavDirections =
ActionViewPagerFragmentToPlantDetailFragment(plantId)
}
}
引数の設定
遷移先の画面のタグ内の<argument>
タグ内に、
遷移先画面で受け取る引数を設定可能
名称 | 内容 | 設定されている値 |
---|---|---|
android:name |
引数の名前 | plantId |
app:argType |
引数の型 | string |
遷移先での引数の取得
遷移先のコードは下記です。
private val args: PlantDetailFragmentArgs by navArgs()
private val plantDetailViewModel: PlantDetailViewModel by viewModels {
InjectorUtils.providePlantDetailViewModelFactory(requireActivity(), args.plantId)
}
公式のドキュメント
https://developer.android.com/guide/navigation/navigation-pass-data?hl=ja#Safe-args
によると、
受信側デスティネーションのコード内で、getArguments() メソッドを使用して、バンドルを取得し、そのコンテンツを使用します。-ktx 依存関係を使用している場合、Kotlin ユーザーは、by navArgs() プロパティ デリゲートを使用して引数にアクセスすることもできます。
なるほど-
ここではsafe args
という機能を利用して、遷移元から渡されたデータを遷移先でargs
という変数で取得し、
それをViewModel
に入れてますねー。
で、画面ではそのViewModel
を介してデータを使用するようになっています。
まとめ
Sunflowerのリポジトリでは
-
Kotlin
でNavigation
を利用している -
Navigation
を利用する時ために、以下のところを追加・変更している- レイアウト
- ナビゲーショングラフ
- 呼び出し元
- 呼び出し先で引数の取得
-
Safe Args
という機能で遷移元から遷移先へデータを渡している
以上です!
参考サイト
- Sunflowerリポジトリ
- Navigation: 公式ドキュメント