Tatsuro のテックブログ

アプリ開発の技術情報を発信します。

【Android × Kotlin】FragmentFactory を使って、コンストラクタ経由で Fragment に値を渡す

今回は、FragmentFactory を使って、コンストラクタ経由で Fragment に値を渡す方法を解説します。

なお、ここに掲載しているソースコードは以下の環境で動作確認しています。

  • Android Studio Chipmunk | 2021.2.1 Patch 1
  • JDK 11.0.12
  • Android Gradle Plugin 7.2.1
  • Kotlin 1.6.21
  • Gradle 7.4.2
  • androidx.fragment 1.4.1

概要

通常、Fragment のコンストラクタに引数を追加することはできません。コンストラクタに引数を追加すると、Fragment を生成する際に例外が発生するからです。

これは、FragmentManager に設定されている FragmentFactory が Fragment を生成するのですが、デフォルトの FragmentFactory は引数が存在しないコンストラクタを使用して、Fragment を生成しようとするためです。

よって、Fragment のコンストラクタに引数を追加して、その引数ありのコンストラクタで Fragment を生成するためには、FragmentFactory を継承したカスタムの FragmentFactory を作成し、これを FragmentManager に設定する必要があります。

今回は、実際に FragmentFactory を継承してカスタム FragmentFactory を作成しながら、FragmentFactory を使って、コンストラクタ経由で Fragment に値を渡す方法を解説します。

ライブラリのインポート

FragmentFactory を使用するため、アプリの build.gradle ファイルの dependenciesfragment-ktx を追加してください。

dependencies {
    implementation "androidx.fragment:fragment-ktx:1.4.1"
}

FragmentFactory を使って、Fragment に値を渡す

今回は、以下のようなサービスクラスをコンストラクタ経由で Fragment に渡すコードを作成します。

interface SampleService {
    fun getText(): String
}

class SampleServiceImpl : SampleService {
    override fun getText(): String = "Sample"
}

まず、Fragment から作成します。

Fragment ではコンストラクタ経由でさきほどの SampleService を受け取りたいため、コンストラクタ引数に SampleService を追加します。

class MainFragment(
    private val service: SampleService
) : Fragment(R.layout.main_fragment) {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val textView: TextView = view.findViewById(R.id.message)
        textView.text = service.getText()
    }
}

次に、FragmentFactory を継承したカスタム FragmentFactory を作成します。

FragmentFactory を使って、引数ありのコンストラクタで Fragment を生成する場合、instantiate をオーバーライドします。このメソッドは、Fragment を生成するメソッドです。カスタム FragmentFactory でオーバーライドした instantiate では、引数 className が該当の Fragment である場合、その Fragment のコンストラクタを呼ぶようにします。そうではない場合、継承元クラスの instantiate を呼ぶようにします。

class CustomFragmentFactory(
    private val service: SampleService
) : FragmentFactory() {

    override fun instantiate(classLoader: ClassLoader, className: String) =
        if (className == MainFragment::class.java.name) {
            MainFragment(service)
        } else {
            super.instantiate(classLoader, className)
        }
}

最後に、Activity を作成します。

Activity では、FragmentManager にさきほどのカスタム FragmentFactory を設定します。なお、このカスタム FragmentFactory の設定は、継承元の onCreate を呼ぶ前に行ってください。

class MainActivity : AppCompatActivity(R.layout.main_activity) {

    override fun onCreate(savedInstanceState: Bundle?) {
        supportFragmentManager.fragmentFactory =
            CustomFragmentFactory(SampleServiceImpl())
        super.onCreate(savedInstanceState)
    }
}

以上で、コンストラクタ経由で Fragment に値を渡せるようになります。

参考

フラグメントに依存関係を渡す