LoginSignup
6

More than 3 years have passed since last update.

posted at

updated at

Organization

[Android] Navigation で画面遷移するときに Safe Args でパラメータを渡す

はじめに

JetpackNavigationSafe Args を使って、
パラメータを渡せるサンプルをサクッと実装したいと思います。

準備

Navigationをセットアップする

Navigation を利用したアプリケーションを作成するための下準備をします。
次の手順で必要なライブラリやクラス、レイアウトを生成していきます。

build.gradle(app)
dependencies {
    def nav_version = "2.1.0"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}

Fragment を作成する

Navigation を使った遷移で利用する Fragment を作成していきます。
次の手順で OneFragment, TwoFragment, ThreeFragment の 3つを作成します。

  1. Package Name を右クリックし、New → Fragment → Fragment(Blank) を選択する
  2. Fragment Name任意の名称 を入力し OK を押す
  3. Fragment にいらない記述が書かれているので消す
  4. LayoutFragment に遷移させるための Button を追加する
  5. LayoutFragment を遷移させたときのパラメータを表示されるTextViewを追加する
Fragment.kt
class OneFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_one, container, false)
    }
}

image.png

layout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
    tools:context=".OneFragment">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="50dp"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_gravity="bottom"
        android:text="Next" />

</FrameLayout>

Safe Argsをセットアップする

あと今回はSafe Argsを利用したアプリケーションを作成するので、
Safe Argsが利用できるようにセットアップします。
この設定をするとSafe Argsで利用するクラスが自動的に生成されるようになります。

build.gradle(.)
    dependencies {
        def nav_version = "2.1.0"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
build.gradle(app)
apply plugin: "androidx.navigation.safeargs.kotlin"

実装

Safe Args を次の 2 ステップで実装していきます。

No 名称 役割
1 Navigationで画面遷移させる Nav HostNavigation GraphNav Controllerを実装して画面遷移できるようにします。
2 Safe Argsでパラメータを渡す Safe Argsで遷移先Fragmentにパラメータを渡せるようにします。

Step1 Navigationで画面遷移させる

Safe Argsでパラメータを引き渡すため基本となる画面遷移を実装していきます。

Nav Graph の作成する

nav_graph.xmlを作成し、次の内容のNavigation Editorで記述します。
Nav Graphの作成の詳しい方法はこちらに書いてあるので参考にしてください。

image.png

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

    <fragment
        android:id="@+id/oneFragment"
        android:name="c.kaleidot725.safeargssample.OneFragment"
        android:label="fragment_one"
        tools:layout="@layout/fragment_one" >
        <action
            android:id="@+id/action_oneFragment_to_twoFragment"
            app:destination="@id/twoFragment" />
    </fragment>
    <fragment
        android:id="@+id/twoFragment"
        android:name="c.kaleidot725.safeargssample.TwoFragment"
        android:label="fragment_two"
        tools:layout="@layout/fragment_two" >
        <action
            android:id="@+id/action_twoFragment_to_threeFragment"
            app:destination="@id/threeFragment" />
    </fragment>
    <fragment
        android:id="@+id/threeFragment"
        android:name="c.kaleidot725.safeargssample.ThreeFragment"
        android:label="fragment_three"
        tools:layout="@layout/fragment_three" />
</navigation>

Nav Hostを作成する

Main ActivityFragmentを表示するためのNav Hostを作成します。

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />
</FrameLayout>

Nav Controller を取得して画面を遷移させる

OneFragment, TwoFragment, ThreeFragment
Nav Controllerを取得して、Buttonで画面遷移できるようにします。

OneFragment.kt
class OneFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_one, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val b = view.findViewById<Button>(R.id.next_button)
        b.setOnClickListener {
            this.findNavController().navigate(R.id.action_oneFragment_to_twoFragment)
        }
    }
}

いまのところ、こんな感じで動作します。このアプリケーションの
画面遷移時にパラメータを引き渡せるように実装していきます。

Dec-08-2019 20-23-31.gif

Step2 Safe Argsでパラメータを渡す

そしてSafe Argsでパラメータを引き渡す処理を実装していきます。

Navigation EditorにてArgumentを作成する

Navigation EditorFragmentArgument Default Valuesを追加して、
遷移元から遷移先に遷移するときにパラメータを指定できるようにします。
次の手順を繰り返してOneFragmentTwoFragmentThreeFragmentにパラメータを指定できるようにします。

  1. 遷移先のFragmentを選択し、AttributesArgumentsをクリックする
  2. NametitleTypeStringを選択し、ADDをクリックする
  3. 1で選択したFragmentに遷移する矢印を選択し、titiledefault valueを設定する。

<Fragment名称>Directions <Fragment名称>Args が生成されているか確認する

Navigation EditorArgumentを作成すると画面遷移時のパラメータ受け渡しに必要となる
<Fragment名称>Directions<Fragment名称>Argsが自動生成されます。

<Fragment名称>Directionsは遷移元のFragmentでパラメータを指定するため、
<Fragment名称>Argsは遷移先のFragmentでパラメータを取得するのに利用します。

今回だとOneFragmentDirecitonsOneFragmentArgsなど、
作成した3つのFragmentに対応するクラスが自動生成されているはずです。

OneFragmentDirections.kt
class OneFragmentDirections private constructor() {
    private data class ActionOneFragmentToTwoFragment(val title: String) : NavDirections {
        override fun getActionId(): Int = R.id.action_oneFragment_to_twoFragment

        override fun getArguments(): Bundle {
            val result = Bundle()
            result.putString("title", this.title)
            return result
        }
    }

    companion object {
        fun actionOneFragmentToTwoFragment(title: String): NavDirections =
                ActionOneFragmentToTwoFragment(title)
    }
}
OneFragmentArgs.kt
data class OneFragmentArgs(val title: String) : NavArgs {
    fun toBundle(): Bundle {
        val result = Bundle()
        result.putString("title", this.title)
        return result
    }

    companion object {
        @JvmStatic
        fun fromBundle(bundle: Bundle): OneFragmentArgs {
            bundle.setClassLoader(OneFragmentArgs::class.java.classLoader)
            val __title : String?
            if (bundle.containsKey("title")) {
                __title = bundle.getString("title")
                if (__title == null) {
                    throw IllegalArgumentException("Argument \"title\" is marked as non-null but was passed a null value.")
                }
            } else {
                throw IllegalArgumentException("Required argument \"title\" is missing and does not have an android:defaultValue")
            }
            return OneFragmentArgs(__title)
        }
    }
}

<Fragment名称>Directionsで遷移時にパラメータを指定する。

次のように<Fragment名称>Directionsを利用して、
画面遷移時にtitleパラメータを指定します。

actionOneFragmentToTwoFragmentにてtitleが指定できます。
今回はTwo Fragmentという文字列を指定しておきます。

OneFragment.kt
class OneFragment : Fragment() {

    val args : OneFragmentArgs by navArgs()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_one, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val b = view.findViewById<Button>(R.id.next_button)
        b.setOnClickListener {
            val action = OneFragmentDirections.actionOneFragmentToTwoFragment("Two Fragment")
            this.findNavController().navigate(action)
        }
    }
}

<Fragment名称>Argsで遷移時にパラメータを取得する。

次のように<Fragment名称>Argsを利用して、
画面遷移先のFragmentにてパラメータを取得します。

TwoFragmentArgsにてtitleが取得できるようになっています。
今回は取得したtitileTextViewに表示してみます。

TwoFragment.kt
class TwoFragment : Fragment() {

    val args : TwoFragmentArgs by navArgs()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_two, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val t = view.findViewById<TextView>(R.id.param_view)
        t.text = args.title
    }
}

こんな感じで画面遷移時にパラメータを渡せるようになります。
遷移先のTextViewに遷移元で指定したパラメータが表示できていますね。

Dec-09-2019 00-10-19.gif

おわりに

上記のサンプルを通して、Safe Argsがどのような動きをするのか、
またはどのようにすれば画面遷移時にパラメータを渡せるのか理解できました。
その内容を簡潔にまとめると次の3点になると思います。

  • NavigationSafe Argsを画面遷移するときにパラメータを受け渡せる。
  • Safe Argsをプロジェクトで利用するようにし、Navigation Editorで遷移時のパラメータを設定すると、自動的にパラメータを受け渡すクラスである<Fragment名称>Directions<Fragment名称>Argsが生成される。
  • 生成された<Fragment名称>Directions<Fragment名称>Argsを利用して、Navigationの画面遷移処理を実装すると安全にパラメータを受け渡すことができる。

Safe Argsを利用する場合のクラス体型を記載すると次のような感じです。
見てわかる通りFragmentごとに必要なクラスが生成されるのでそれを使えばよいようになっています。

image.png

参考文献

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
6