2021年4月11日日曜日

DialogFragmentは別ファイルに分けないとクラッシュする

前回、Fragmentはpublicクラスで定義しないと落ちる話をした。
当然、DialogFragmentもFragmentを継承しているので、この制約を受ける。。
よって、ファイルを分けてpublicクラスでDialogFragmentも定義しなくてはクラッシュすることになる。

しかし、ダイアログなんかは軽く表示させたいし、
わざわざクラスファイルを分けて記述していくのが面倒くさい。。
なので、ファイルを分けなくてすむ方法について記載する。


AlertDialogをそのまま使う

DialogFragmentを経由せず、そのままアラートダイアログを使ってshowメソッドを呼び出せばダイアログが表示できる。
つまり、ボタンを押したらダイアログ表示させるプログラムなら以下で良い
findViewById(R.id.button).setOnClickListener(v ->
   new AlertDialog.Builder(this)
                    .setTitle("dialog").setMessage("dialog").show());
しかしながら、これはアンチパターン。
公式ページにも、直接AlertDialogを実行せず、DialogFragmentを経由する旨が記載されている。

何故DialogFragmentを使うべきなのかは、メモリ制御にある。
showされたDialogは閉じる必要があるのだが、
画面回転等で強制的に閉じられた際、適切に処理しないとインスタンスが残り続けるらしい。この辺の制御を実装者独自で行うのは酷なので、制御をFragmentにゆだねるDialogFragmentが推奨されている。

そうは言っても、大量にアラートを出したり、とても重いアラートを生成していなければメモリ枯渇したりするケースはないだろうから、「実装方法として有効」ということで記述した。


DialogFragmentを1ファイル内に収める

前のURL先にも書いたが、Fragmentを外部からインスタンスを生成できるようにする必要がある。java言語には、「publicクラスは同じファイル内に2つ存在できない」ルールがあるので、インナークラスを用いて定義する方法を取る。
つまり、以下のようにすれば、MainActivityのファイル内にFragmentが共存できる。
public class MainActivity extends AppCompatActivity {
    /** ダイアログを定義 */
    public static class MyDialogFragment extends DialogFragment {
        @Override @NonNull
        public Dialog onCreateDialog (Bundle savedInstanceState) {
            return new AlertDialog.Builder(getActivity())
                    .setTitle("dialog").setMessage("dialog").create();
        }
    }
}

ポイントは
インナークラスを「public static」にすること。こうすることで外部からもインナークラスのコンストラクタが呼び出せるようになる。


以上二つの方法(主に後者)で実装すれば、
Dialogのためにファイルを分ける手間が省ける。

0 件のコメント:

コメントを投稿