Open4

[Android] 汎用的な DialogFragment にホストから動作を注入したい

ymdkitymdkit

目的

  • 汎用的な DialogFragment にホストから動作を注入したい
    • 削除確認等の DialogFragment を共通化したい

理想

// HostFragment
fun delete(id){
  ConfirmDangerDialogFragment.newInstance(
            title = "削除しますか?",
            completion = {
              viewModel.delete(id)
            }
  ).show(childFragmentManager, ConfirmDangerDialogFragment::class.java.name)
}

問題点

Activity が破棄された場合、再生成のタイミングで completion の内容が失われる(クロージャは bundle に保存できない)
→ ダイアログの見た目は復元されるが、ボタンを押しても何も起こらない

ymdkitymdkit

解決策1

呼び出し元の Activity(Fragment) にリスナーを implements する

https://developer.android.com/guide/topics/ui/dialogs?hl=ja#PassingEvents

問題点

  • onAttach で復元する関係上、無名クラスで渡すことができない
    • ソースコードが散らばってしまい、処理の流れを追いにくい
  • 呼び出し側が送ったパラメータ(削除するもの)を意識する必要があるため、ダイアログの汎用性が損なわれる(「〇〇を削除するためのダイアログ」というのをダイアログ側が意識する必要がある)
ymdkitymdkit

解決策2

setFragmentResultListener を利用する

https://developer.android.com/training/basics/fragments/pass-data-between?hl=ja#child

実装例

Fragment のインスタンス生成時に setFragmentResultListener を行なっている

https://zenn.dev/m_coder/articles/article-zenn-custom-dialog-by-dialogfragment

問題点

  • Activity が破棄されると listener が破棄されるので、イベントがトリガーされない
    • 加えて、二回目にダイアログを開いたタイミング(setFragmentResultListener したタイミング)でイベントが発火してしまう
  • → setFragmentResultListener は onCreate で呼び出す必要がある
    • 解決策1と同じ問題に直面する
ymdkitymdkit

解決策3

中途半端に再生成されるのが問題なので、 Activity が再生成された場合に Dialog を復元しないようにする

    // DialogFragment
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (savedInstanceState != null){
            dismiss()
        }
    }

問題点

  • 一般的なフラグメントのライフサイクルに沿っていない
  • 画面回転でダイアログが消えてしまう(実装中のアプリは縦画面固定なのでここは問題なさそう?)
  • 再生成が起きるタイミングはこちらでは制御できないため、意図しないタイミングでダイアログが消える可能性がある。