はじめに
Android でも iOS でいう UINavigationController
のようなことができるのが Navigation
。(たぶん)
Navigation Graph に複数の Navigation Graph を置くこともできるみたい(たぶん Storyboard Reference みたいなの)ですが今回は単一のやつのみの単純な遷移のみについて記載します。ついでに NavigationView
と BottomNavigationView
についても記載します。
最終的にはこんな感じ
NavigationView | BottomNavigationView |
---|---|
遷移
ソースはここ↓↓↓ (これの NavigationActivity とか)
ソース(github)
Navigation
必要なのは下記
- Activity
NavHostFragment を置く Activity - Fragment
Navigation で遷移するそれぞれのページ - Navigation Graph
それぞれのページの遷移先を示した xml - NavHost
Navigation Graph からの宛先を表示する空のコンテナ(NavHostFragment) - NavController
NavHost の画面遷移をコントロールするやつ
遷移の実装
- Navigation Graph 追加
- Activity に NavHostFragment を置く
- Fragment 追加
- Navigation Graph で Action と Destination を設定
- 遷移処理実装
1. Navigation Graph 追加
下記のように res に navigation.xml を追加する
2. Activity に NavHostFragment を置く
任意の Activity に NavHostFragment を追加して navGraph に 1. で追加した navigation を指定する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <fragment android:id="@+id/fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:defaultNavHost="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/navigation" /> </androidx.constraintlayout.widget.ConstraintLayout> |
defaultNavHost
に true
を設定するとバックキーを押したときに Navigation
にスタックがある場合はその Fragment
に戻りない場合は Activity
が終了する。false
を設定した場合はスタックがあってもなくても Activity
が終了する。
3. Fragment 追加
3つのページとして NavigationFirstFragment
, NavigationSewcondFragment
, NavigationThirdFragment
を追加する。
First
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent" android:layout_height="match_parent" android:background="#ff3b30" tools:context=".navigation.NavigationFirstFragment"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:gravity="center_horizontal" android:text="First" android:textColor="#fff" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:layout_marginBottom="16dp" android:text="To Second" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> |
Second
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent" android:layout_height="match_parent" android:background="#007aff" tools:context=".navigation.NavigationSecondFragment"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center_horizontal" app:layout_constraintBottom_toTopOf="@+id/button_back" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Second" android:textColor="#fff"/> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#fff" tools:text="Test" /> </LinearLayout> <Button android:id="@+id/button_back" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:layout_marginBottom="16dp" android:text="Pop" app:layout_constraintBottom_toTopOf="@+id/button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> <Button android:id="@+id/button" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:layout_marginBottom="16dp" android:text="To Third" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> |
Third
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent" android:layout_height="match_parent" android:background="#34c759" tools:context=".navigation.NavigationThirdFragment"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center_horizontal" app:layout_constraintBottom_toTopOf="@+id/button_back" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Third" android:textColor="#fff"/> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#fff" tools:text="Test" /> </LinearLayout> <Button android:id="@+id/button_back" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:layout_marginBottom="16dp" android:text="Pop" app:layout_constraintBottom_toTopOf="@+id/button_back_first" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> <Button android:id="@+id/button_back_first" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:layout_marginBottom="16dp" android:text="Back To First" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> |
4. Navigation Graph で Action と Destination を設定
navigation.xml を開き下記のように+を押して NavigationFirstFragment
, NavigationSewcondFragment
, NavigationThirdFragment
を追加する。
First と Second、Second と Third を下記のように結ぶ。
navigation.xml はこんな感じになる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?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/navigation" app:startDestination="@id/navigationFirstFragment"> <fragment android:id="@+id/navigationFirstFragment" android:name="com.example.widgetsample.navigation.NavigationFirstFragment" android:label="fragment_navigation_first" tools:layout="@layout/fragment_navigation_first" > <action android:id="@+id/action_navigationFirstFragment_to_navigationSecondFragment" app:destination="@id/navigationSecondFragment" /> </fragment> <fragment android:id="@+id/navigationSecondFragment" android:name="com.example.widgetsample.navigation.NavigationSecondFragment" android:label="fragment_navigation_second" tools:layout="@layout/fragment_navigation_second" > <action android:id="@+id/action_navigationSecondFragment_to_navigationThirdFragment" app:destination="@id/navigationThirdFragment" /> </fragment> <fragment android:id="@+id/navigationThirdFragment" android:name="com.example.widgetsample.navigation.NavigationThirdFragment" android:label="fragment_navigation_third" tools:layout="@layout/fragment_navigation_third" > </fragment> </navigation> |
5. 遷移処理実装
それぞれの Fragment
に遷移処理を実装する。
First
1 2 3 4 5 6 |
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.findViewById<Button>(R.id.button).setOnClickListener { findNavController().navigate(R.id.action_navigationFirstFragment_to_navigationSecondFragment) } } |
Second
1 2 3 4 5 6 7 8 9 |
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.findViewById<Button>(R.id.button).setOnClickListener { findNavController().navigate(R.id.action_navigationSecondFragment_to_navigationThirdFragment) } view.findViewById<Button>(R.id.button_back).setOnClickListener { findNavController().popBackStack() } } |
Third
1 2 3 4 5 6 7 8 9 |
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.findViewById<Button>(R.id.button_back_first).setOnClickListener { findNavController().popBackStack(R.id.navigationFirstFragment, false) } view.findViewById<Button>(R.id.button_back).setOnClickListener { findNavController().popBackStack() } } |
findNavController()
で NavController
を取得して navigate
で遷移する。戻るときは popBackStack
で戻る。
これで Navigation
を使った遷移処理は完了!!
アニメーションもつけれるらしい。(参考:[Android] 10分で作る、Navigationによる画面遷移)
値渡しの実装
ページ間で値を渡す方法は Bundle
と Safe Args を利用する方法の2つがある。
渡せる値は下記。
- Integer
- Float
- Long
- Boolean
- String
- リソース参照
- カスタム Parcelable
- カスタム Serializable
- カスタム Enum
Bundle 利用
Bundle 利用の場合は下記のような感じ。
送信側
1 2 |
val param = bundleOf("name" to "名前", "age" to 20) findNavController().navigate(R.id.action_navigationFirstFragment_to_navigationSecondFragment, param) |
受信側
1 2 3 4 5 6 7 |
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) arguments?.let { val name = it.getString("name") val age = it.getInt("age") } } |
Safe Args 利用
Safe Args を使う場合は色々と前準備がいるけどこいつを使う方がよさそう。
- プロジェクトの build.gradle に依存関係記載
- モジュールの build.gradle に依存関係記載
- Navigation Graph に Arguments 追加
- 値の受け渡し処理実装
1. プロジェクトの build.gradle に依存関係記載
プロジェクトの build.gradle の dependencies
に下記を追記する
1 2 |
def nav_version = "2.3.0" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" |
2. モジュールの build.gradle に依存関係記載
モジュールの build.gradle に下記を追記する
1 |
apply plugin: "androidx.navigation.safeargs.kotlin" |
3. Navigation Graph に Arguments 追加
navgation.xml を開いて値を渡す Fragment (Second)を選択して Arguments の+を押下して Arguments を追加する。
下記のように navgation.xml の Second のところに argument
タグが追加される
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<fragment android:id="@+id/navigationSecondFragment" android:name="com.example.widgetsample.navigation.NavigationSecondFragment" android:label="fragment_navigation_second" tools:layout="@layout/fragment_navigation_second" > <action android:id="@+id/action_navigationSecondFragment_to_navigationThirdFragment" app:destination="@id/navigationThirdFragment" /> <argument android:name="name" app:argType="string" /> <argument android:name="age" app:argType="integer" android:defaultValue="0" /> </fragment> |
これで一度ビルドすると java(genarated) のフォルダに ~Directions と ~Args というファイルが生成される。
4. 値の受け渡し処理実装
値の受け渡しは下記のようにおこなう。
送信側
1 2 |
val action = NavigationFirstFragmentDirections.actionNavigationFirstFragmentToNavigationSecondFragment("名前", 20) findNavController().navigate(action) |
受信側
1 2 3 |
val navArgs: NavigationSecondFragmentArgs by navArgs() val name = navArgs.name val age = navArgs.age |
割と簡単だしコードもスッキリするので Safe Args を使えばいいと思います!
NavigationView
左端を右にスワイプして出てくるドロワー?Navigation
と併用するやつなのか知りませんが Navigation
との併用方法について記載します。
- メニューの xml ファイルを追加
- レイアウトに NavigationView を追加
- Navigation と紐付ける
1. メニューの xml ファイルを追加
下記のように menu_navigation.xml を追加する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <group android:checkableBehavior="single"> <item android:id="@+id/navigationFirstFragment" android:icon="@android:drawable/ic_menu_gallery" android:title="First" /> <item android:id="@+id/navigationSecondFragment" android:icon="@android:drawable/ic_menu_gallery" android:title="Second" /> <item android:id="@+id/navigationThirdFragment" android:icon="@android:drawable/ic_menu_gallery" android:title="Third" /> </group> </menu> |
id は navigation.xml のそれぞれの fragment と揃える。
2. レイアウトに NavigationView を追加
NavigationView
を利用する場合はレイアウトのルートを DrawerLayout
にする必要があるので下記のようにして NavigationView
を追加する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?xml version="1.0" encoding="utf-8"?> <androidx.drawerlayout.widget.DrawerLayout 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:layout_width="match_parent" android:layout_height="match_parent" 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/navigation" /> <com.google.android.material.navigation.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" app:headerLayout="@layout/navigation_drawer_header" app:menu="@menu/menu_navigation"/> </androidx.drawerlayout.widget.DrawerLayout> |
headerLayout
は特に設定しなくてもいいが今回は下記のような xml を設定しました。これでドロワーのヘッダーが表示されます。
navigation_drawer_header.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent" android:layout_height="160dp" android:background="@color/colorAccent"> <ImageView android:id="@+id/imageView" android:layout_width="120dp" android:layout_height="120dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/ic_launcher_foreground" /> </androidx.constraintlayout.widget.ConstraintLayout> |
3. Navigation と紐付ける
Activity
の onCreate
に下記を追記して Navigation
と紐付ける。
1 2 3 |
val navController = findNavController(R.id.fragment) findViewById<NavigationView>(R.id.nav_view) .setupWithNavController(navController) |
これでコードで遷移した場合もメニューの選択状態が反映される。
BottomNavigationView
名前の通り Bottom
に表示するやつ。(TabBar みたいなもの?)
- メニューの xml ファイルを追加
- レイアウトに BottomNavigationView を追加
- Navigation と紐付ける
1. メニューの xml ファイルを追加
NavigationView
と同じ menu_navigation.xml を利用する
2. レイアウトに BottomNavigationView を追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".NavigationActivity"> <fragment android:id="@+id/fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:defaultNavHost="true" app:layout_constraintBottom_toTopOf="@id/bottom_nav_view" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/navigation" /> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_nav_view" android:layout_width="0dp" android:layout_height="wrap_content" android:background="@color/colorAccent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:menu="@menu/menu_navigation" /> </androidx.constraintlayout.widget.ConstraintLayout> |
3. Navigation と紐付ける
Activity
の onCreate
に下記を追記して Navigation
と紐付ける。
1 2 3 |
val navController = findNavController(R.id.fragment) findViewById<BottomNavigationView>(R.id.bottom_nav_view) .setupWithNavController(navController) |
これでコードで遷移した場合もメニューの選択状態が反映される。
おわりに
これで Android の画面遷移は大体理解した!(気がする)Activity 1つで画面はそれぞれ Fragment にするのと画面毎に Activity を作るのとできるけどどっちがスタンダードなんだろう??
参考
- Androidデベロッパー:ナビゲーション
- [Android] 10分で作る、Navigationによる画面遷移
- NavigationViewを使ってGoogle Play風のメニューを作る【Android Design Support Library】
- Navigationを使ったアプリ開発
- Navigation(AAC)でBottomNavigationどうやるの
- [Android] Navigation で画面遷移するときに Safe Args でパラメータを渡す
- AndroidX Navigation による画面遷移の実装まとめ
コメント
[…] Navigation はこっち […]