Open4

Navigationを導入する

公式Doc: Navigation

導入

  // Kotlin
  implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
  implementation("androidx.navigation:navigation-ui-ktx:$nav_version")

  // Feature module Support
  implementation("androidx.navigation:navigation-dynamic-features-fragment:$nav_version")

  // Testing Navigation
  androidTestImplementation("androidx.navigation:navigation-testing:$nav_version")

Mavenによると、最新版は2.3.5らしい。

  • Navigation Graph: すべてのDestinationとActionを格納するリソースファイル。アプリ内のパスを示す。
  • Destination: アプリ内の様々なコンテンツ領域を示す
  • Action: Destination間の論理接続を示し、ユーザーが移動可能なパスを表現する

resディレクトリにNavigationGraphを追加するには、 New > Android Resource FileResource type: Navigationを選択して新規リソースとしてnavigation_graph.xmlを追加する。
-> Navigation Editorが使えるようになった。けどまだ空っぽ。

activityに以下を追加。

    <androidx.fragment.app.FragmentContainerView
        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/nav_graph" />
  • android:nameはNavHostの実装クラス名
  • app:defaultNavHost="true"はシステムの戻るボタンをインターセプトできるようになる。NavHostは1つだけデフォルれる。

LayoutEditoerからNaviHostを追加することもできる。LayoutEditor > Palette > Containers > NavHostFragment

疑問: widthとheightはmatch_parentじゃなくて0dpでいいのかな?
ConstraintLayoutはmatch_parentmatch_constraintsになってて、設定するには0dpを指定するみたい。
Yukiの枝折- ConstraintLayout

NavigationEditor > New Destination > Create new destination でFragmentのタイプを選んでcreate。
Placeholder Destinationってなに?

プレースホルダを使用して、未実装のデスティネーションを示すことができます。 プレースホルダは、デスティネーションの視覚的な表現として機能します。Navigation Editor 内では、通常のデスティネーションと同様に、プレースホルダを使用できます。
注: アプリを実行する前に、プレースホルダの class 属性を既存のデスティネーションに変更する必要があります。プレースホルダによってコンパイル エラーが発生することはありません。ただし、プレースホルダ デスティネーションに移動しようとすると、アプリはランタイム例外をスローします。

Destinationの構造

項目 説明
id Destinationを参照する際に使用するid
Label ユーザが読むことのできる形式でDestinationの名前が表示される。これはUIに表示させることも可能。(setupWithNavController()を使用してNavGraphをToolbarに接続する場合)。リソース文字列の使用を推奨。
name 説明無し。Fragmentのpathっぽい
class 存在してないが説明あり。関連付けられてるクラス名
type 存在してないが説明あり。実装タイプがFragmentなのかActivityなのかカスタムなのか。型?

説明がupdateされてなさそうだけど、旧type+class = nameになってる感じか。
NavigationEditorから各Destination(Fragment)のcodeも参照できて便利。

Start Destination

ユーザがアプリを開いた際に最初に表示される画面のこと。家アイコンで指定できる。
指定するとnavGraphのapp:startDestinationに追加されてた。

<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_graph"
    app:startDestination="@id/topFragment">

Destinationを接続する

EditorでFragmentをつなぐことで移動先を定義できる。
各画面の共通の移動先としてグローバルアクションを定義することもできる。

Destinationに移動する

移動はNavHost内でNavigationを管理するNavControllerを使用して実行される。各NavHostはそれぞれ独自のNavControllerを持つ。

// NavControllerの取得
Fragment.findNavController()
View.findNavController()
Activity.findNavController(viewId: Int)

FragmentContainerView を使用して NavHostFragment を作成する場合、または FragmentTransaction からアクティビティに NavHostFragment を手動で追加する場合、Navigation.findNavController(Activity, @IdRes int) からアクティビティの onCreate() で NavController を取得しようとするとエラーになります。NavHostFragment から直接 NavController を取得してください。

val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController

Safe Args

Destination間を移動する際は、Safe Args Gradle Pluginの使用が推奨されている。これはDestination間で型安全なNavigationと引数の受け渡しを可能にする。

追加
module build.gradle

buildscript {
    repositories {
        google()
    }
    dependencies {
        def nav_version = "2.3.5"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

app build.gradle

// for Java and Kotlin
plugins {
  id 'androidx.navigation.safeargs'
}
// for Kotlin
plugins {
  id 'androidx.navigation.safeargs.kotlin'
}

gradle.properties ファイルに android.useAndroidX=true が必要。

Safe Args を有効にすると、このプラグインによって、定義した各アクションのクラスとメソッドを格納するコードが生成されます。各アクションに対し、Safe Args は、アクションの発生元となる送信側デスティネーションごとにクラスを生成します。生成されるクラス名は、送信側デスティネーションのクラス名に「Directions」という語を組み合わせたものになります。たとえば、送信側デスティネーションが「SpecifyAmountFragment」という名前の場合、「SpecifyAmountFragmentDirections」という名前のクラスが作成されます。生成されるクラスには、送信側デスティネーション内で定義されている各アクション用の静的メソッドが格納されます。このメソッドは、定義済みのアクション パラメータを引数として受け取り、NavDirections オブジェクトを返します。このオブジェクトは navigate() に渡すことができます。

override fun onClick(view: View) {
    val action =
        SpecifyAmountFragmentDirections
            .actionSpecifyAmountFragmentToConfirmationFragment()
    view.findNavController().navigate(action)
}

値を渡す方法は「Safe Args を使用してタイプセーフにデータを渡す」を参照。

ログインするとコメントできます