It’s now or never

IT系の技術ブログです。気になったこと、勉強したことを備忘録的にまとめて行きます。

【Android】【Jetpack】Navigationコンポーネントの使い方 ①

概要

Android Jetpackの機能の一つに「Navigation コンポーネント」という画面遷移を管理してくれる機能があります。
Navigationコンポーネント自体は発表されて数年たちそれほど最新の話ではないのですが、僕自身は触ったことがなかったので改めて触ってみました。

環境のセットアップ

まずは app/build.gradle に依存モジュールを追加します。
現時点での最新の安定版は「2.2.2」なのですが、「2.3.0」から navigation-dynamic-features-fragment という新しいモジュールが追加されています。
使い方は現時点ではまだ良くわかってませんが、動作検証なので最新のモジュールバージョン「2.3.0-alpha06」を使ってみようと思います。

dependencies {
・・・
    def nav_version = "2.3.0-alpha06"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
    implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"
    androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
・・・
} 

次に Safe Args Gradle プラグイン を追加します。
このプラグインは必須ではないのですが、これを使うと画面遷移処理が安全に行えるようになるので入れておきます。
まず ルートディレクトリのbuild.gradle に以下を追加します。

buildscript {
    repositories {
        // 記載がなければ追加
        google()
    }
    dependencies {
        def nav_version = "2.3.0-alpha06"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

次に app/build.gradleプラグインの適用を記載します。

apply plugin: "androidx.navigation.safeargs.kotlin"

以上で、環境設定はおわりです。

「ナビゲーショングラフ」を作成する

NavigationComponentを使い始めるためにまずは、ナビゲーショングラフ というものを作成します。
ナビゲーショングラフは、xmlで書かれたリソースファイルで、画面遷移の関連を記載したファイルです。
まずは、リリースファイルにこのxmlを追加します。

  • AndroidStudioから、resディレクトリを右クリックして、「New > Android Resource File」 を選択してダイアログを表示します。
  • File name は任意ですが、今回は「nav_graph」にしています。
  • Resource type には Navigation を選択します

f:id:inon29:20200607104334p:plain

f:id:inon29:20200607104412p:plain

resディレクトリには、navigation というディレクトリが作成されその中に「nav_graph.xml」というファイルが作成されます。
このファイルを開くとAndroidStudio上でビジュアルエディタによってナビゲーショングラフを編集することができます。

遷移するフラグメントを作成

今回遷移するサンプル画面として

  • FirstFragment
  • SecondFragment

という2つのフラグメントを用意します。

フラグメントは、先程作成した nav_graph のメニューから「Create new destination」というメニューを選択すると作成できます。
(普通にファイル追加で作成しても大丈夫ですが、ナビゲーショングラフのメニューから作成すると、自動でナビゲーショングラフのエディター内に追加してくれます)

f:id:inon29:20200607104816p:plain

f:id:inon29:20200607104827p:plain

FirstFragmentのViewには「家のアイコン」がついています。
これは、一番最初に表示される画面を指していて、後で変更することもできます。

画面間の移動を関連付け

2つのフラグメントの遷移の関連付けを行います。
ナビゲーショングラフのエディタ上でFirstFragmentからSecondFragmentに対して関連をドラッグすることで2つの画面の遷移を関連づけることができます。

f:id:inon29:20200607104950p:plain

以上で、ナビゲーショングラフの設定は完了です。

このとき、nav_graph.xmlの中身は次のようになっています。

■ nav_graph.xml

<?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.nav2.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.nav2.SecondFragment"
        android:label="fragment_second"
        tools:layout="@layout/fragment_second" />
</navigation>

app:startDestination="@id/firstFragment" は初期起動画面の指定。

<action
    android:id="@+id/action_firstFragment_to_secondFragment"
    app:destination="@id/secondFragment" />

このactionタグでどの画面からどの画面への遷移を紐付けているようです。

起動時のActivityに「ナビゲーションホスト」を作成する

ナビゲーショングラフを作ったのでそれをアプリケーションに適用する作業をしていきます。

ナビゲーショングラフを適用するには ナビゲーションホスト を作る必要があります。
ナビゲーションホストは、ナビゲーショングラフで表現された画面の遷移を実行して中身のフラグメントを書き換えるためのコンテナです。
このコンテナを配置すれば画面遷移の管理は、ナビゲーションホストがよしなにやってくれるというものらしいです。

これを、起動されるActivityにセットしていきます。
今回のアクティビティ(MainActivity)のlayout xmlは次のようになっています。

■ actibity_main.xml

<?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/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" />

</androidx.constraintlayout.widget.ConstraintLayout>

ナビゲーションホストには、androidx.navigation.fragment.NavHostFragment クラスを使用します。
app:navGraph="@navigation/nav_graph"属性では、ナビゲーショングラフのxmlファイルを指定します。
app:defaultNavHost="true"属性は、システムの戻るボタン(バックボタン)をインターセプトするためのフラグで、trueにするとバックボタンを押下したときにFragmentの前画面へ遷移してくれます。

Activity側の設定はこれだけです。
この状態でアプリを起動すると、ナビゲーショングラフで設定した startDestination (家のアイコンがついているフラグメント) が初期表示されます。

画面遷移を実装

最後に FirstFragmentからSecondFragmentへの画面遷移を実装します。

まずは、遷移元であるFirstFragmentに遷移のトリガーとしてボタンを配置します。

■ fragment_first.xml

<?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:layout_gravity="center"
        android:textSize="30dp"
        android:textAlignment="center"
        android:text="First Fragment" />

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Move to SecondFragment" />
</LinearLayout>

次に、FirstFragment.kt上に遷移処理を書いていきます。

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val view = inflater.inflate(R.layout.fragment_first, container, false)
    view.findViewById<Button>(R.id.button) .setOnClickListener {
        val action = FirstFragmentDirections.actionFirstFragmentToSecondFragment()
        view.findNavController().navigate(action)
    }
    return view
}

各画面間の遷移処理は、NavController を使って行います。
view.findNavController() で NavControllerを取得し、navigate メソッドで遷移を実行しています。

actionを取得する処理として FirstFragmentDirections.actionFirstFragmentToSecondFragment() というメソッドを使用しています。
これは、一番はじめの環境設定時に適用した Safe Args Gradle プラグイン による遷移方法です。
このプラグインを使うと <遷移元クラス名>Directions というクラス(今回はFirstFragmentDirections)がコンパイル時に自動生成され、IDの指定などを行わずに安全に遷移を行うことができます。

ちなみにプラグインを使わない場合は、次のように書くことも可能です。

view.findViewById<Button>(R.id.button) .setOnClickListener {
    view.findNavController().navigate(R.id.action_firstFragment_to_secondFragment)
}

R.id.action_firstFragment_to_secondFragment のIDは、ナビゲーショングラフのxmlファイル(nav_graph.xml)でactionタグに設定されているIDです。

以上で実装は完了です。

アプリを起動して、「Move to SecondFragment」というボタンを押下するとSecondFragmentへ移動が確認できます。

まとめ

今更ですが、Navigation コンポーネントについてチュートリアルをやってみました。

AndroidにおけるFragmentの遷移は、状態管理が複雑でAndroidを開発初めて開発するときは必ずと言っていいほどつまずくポイントの一つでしたが、Navigationコンポーネントを使うことでこのあたりの学習コストが大幅に減らせるようになったと感じました。
これからAndroidの開発をしていく人は、積極的に使っていくのが良さそうです。

パラメタの引き渡し方法や、他Activityとの絡みなどまだ疑問な点もあるのでまた触ってみたいと思います。

参考リンク