Android(Kotlin)Navigation はじめの一歩
August 27, 2020
はじめに
アプリバーを実装したくて色々調べていたら、 Navigation コンポーネントの存在を知りました。 ドキュメントを見る限りは便利そうだったので、導入検討のため、最低限の画面遷移処理を実装してみました。
実装内容
Gradle
Gradle プラグインを追加するので、 モジュールレベルビルドファイルだけでなく、 トップレベルビルドファイルも修正します。
build.gradle
Safe Args
Gradle プラグインを追加します。
また、モジュールレベルビルドファイルとバージョンを合わせたいので、バージョン情報も記述しておきます。
@@ -1,11 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.4.0"
+ ext.nav_version = "2.3.0"
repositories {
google()
jcenter()
}
dependencies {
+ classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
classpath "com.android.tools.build:gradle:4.0.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
app/build.gradle
Kotlin 向け Safe Args
プラグインを適用します。プラグイン適用を記述する箇所ですが、どうやら apply plugin:
記述は順番も重要らしく、一番最初に記述したりすると、safeargs plugin must be used with android plugin
とエラーが出てしまうのでご注意ください。
また、Kotlin 向け依存関係も追加します。
@@ -1,6 +1,7 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
+apply plugin: 'androidx.navigation.safeargs.kotlin'
android {
compileSdkVersion 29
@@ -29,6 +30,8 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
+ implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
ナビゲーショングラフ
app/src/main/res/layout/activity_main.xml
Navigation コンポーネントのデフォルト NavHost 実装である NavHostFragment を Activity に追加します。この箇所で画面(正確にはデスティネーション)が切り替わることになります。
@@ -7,4 +7,11 @@
android:orientation="vertical"
tools:context=".MainActivity">
+ <fragment
+ android:id="@+id/fragment"
+ android:name="androidx.navigation.fragment.NavHostFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:defaultNavHost="true"
+ app:navGraph="@navigation/nav_graph" />
</LinearLayout>
app/src/main/res/navigation/nav_graph.xml
Android Studio において GUI ベースで作成します。作業フローの中で追加デスティネーションとしてフラグメントを生成することもできますし、最初にフラグメントを作ってしまって、デスティネーションとして追加することもできます。
ここでは後述する二つのフラグメントをお互いに遷移できるように設定しました。
@@ -0,0 +1,26 @@
+<?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"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/nav_graph"
+ app:startDestination="@id/firstFragment">
+
+ <fragment
+ android:id="@+id/firstFragment"
+ android:name="com.example.myapplication.FirstFragment"
+ android:label="fragment_first"
+ tools:layout="@layout/fragment_first" >
+ <action
+ android:id="@+id/action_firstFragment_to_secondFragment"
+ app:destination="@id/secondFragment" />
+ </fragment>
+ <fragment
+ android:id="@+id/secondFragment"
+ android:name="com.example.myapplication.SecondFragment"
+ android:label="fragment_second"
+ tools:layout="@layout/fragment_second" >
+ <action
+ android:id="@+id/action_secondFragment_to_firstFragment"
+ app:destination="@id/firstFragment" />
+ </fragment>
+</navigation>
デスティネーション用フラグメント
遷移を確認できるように、TextView
と Button
を持つだけのデスティネーション用フラグメントを二つ作成します。
遷移時に引数として必要な action
を Safe Args
のおかげで簡単に記述することができます。
app/src/main/java/com/example/myapplication/FirstFragment.kt
@@ -0,0 +1,25 @@
+package com.example.myapplication
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import androidx.fragment.app.Fragment
+import androidx.navigation.findNavController
+
+class FirstFragment : Fragment() {
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return inflater.inflate(R.layout.fragment_first, container, false).apply {
+ findViewById<Button>(R.id.first_button).apply {
+ setOnClickListener {
+ val action = FirstFragmentDirections.actionFirstFragmentToSecondFragment()
+ it.findNavController().navigate(action)
+ }
+ }
+ }
+ }
+}
app/src/main/java/com/example/myapplication/SecondFragment.kt
@@ -0,0 +1,25 @@
+package com.example.myapplication
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import androidx.fragment.app.Fragment
+import androidx.navigation.findNavController
+
+class SecondFragment : Fragment() {
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return inflater.inflate(R.layout.fragment_second, container, false).apply {
+ findViewById<Button>(R.id.second_button).apply {
+ setOnClickListener {
+ val action = SecondFragmentDirections.actionSecondFragmentToFirstFragment()
+ it.findNavController().navigate(action)
+ }
+ }
+ }
+ }
+}
app/src/main/res/layout/fragment_first.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context=".FirstFragment">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="first_fragment" />
+
+ <Button
+ android:id="@+id/first_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button" />
+
+</LinearLayout>
app/src/main/res/layout/fragment_second.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context=".SecondFragment">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="second_fragment" />
+
+ <Button
+ android:id="@+id/second_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button" />
+
+</LinearLayout>