カテゴリー
Kotlin言語

Android(Kotlin):Dialog の実装のしかた

ダイアログ(AlertDialog)を DialogFragment を使って Android アプリに実装する方法です。Android Developers 公式の解説はこちらからどうぞ


ダイアログ作成の準備

Android Studio で作業しますね

  • ダイアログ用のクラスの作成・・・app > java > パッケージ名 とたどって右クリック、New > Kotlin Class/File でファイルを生成します
  • ダイアログ用のカスタムレイアウトの作成(XML)・・・res > layout とたどって右クリック、New > Android Resource File でXMLファイルを生成して、いつも通りレイアウトを作成します

ダイアログ作成に必要なもの

基本的なダイアログならクラスファイルだけでいいの

  • 基本的なダイアログは、ダイアログ用のクラスを作成して、それを呼び出すだけで利用できます
  • 基本的なダイアログに、XMLレイアウトを使って作成したUI部品(View)を追加することもできます

ダイアログ用のクラスの書き方

AlertDialog の作り方です

  • 基本的なダイアログは、通常 AlertDialog からインスタンスを生成します
    • AlertDialog は Dialog クラスのサブクラスですが、インスタンスは Dialog クラスから直接生成しないようにします
    • AlertDialog 以外に DatePickerDialog や TimePickerDialog からインスタンスを生成することもできます
  • ダイアログ用のクラスは DialogFragment を継承して作成します
    • 利用にあたっては android.app.DialogFragment ではなく android.support.v4.app.DialogFragment をインポートします
  • ダイアログの生成は、オーバーライドでこのクラスに onCreateDialog() メソッドを作成し、その中で AlertDialog を使用して設定します

基本的な書き方

たとえば、次のようになります

activity プロパティを使ってみますね

class SampleDialog : DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle): Dialog {
        return activity?.let {
            val builder = AlertDialog.Builder(it)
            builder.setMessage("Sample Message")
                    .setPositiveButton("OK",
                            DialogInterface.OnClickListener { dialog, id ->
                                // OKボタンが押されたときの処理
                            })
                    .setNegativeButton("Cancel",
                            DialogInterface.OnClickListener { dialog, id ->
                                // キャンセルボタンが押されたときの処理
                            })
            builder.create()
        } ?: throw IllegalStateException("Activity cannot be null")
    }
}
  • フラグメントが関連付けられているアクティビティを参照するのに activity プロパティを使っています
  • activity は nullable なので activity? とし、さらに拡張関数let() と throw を使って null だった場合の処理を記述しています

requireActivity() や requireContext() を使う

  • requireActivity() や requireContext() を使ってみます
  • アクティビティが null の場合は例外が発生します

requireContext() を使ってみました

class SampleDialog : DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle): Dialog {
        val builder = AlertDialog.Builder(requireContext())
        builder.setMessage("Sample Message")
            .setPositiveButton("OK",
                    DialogInterface.OnClickListener { dialog, id ->
                        // OKボタンが押されたときの処理
                    })
            .setNegativeButton("Cancel",
                    DialogInterface.OnClickListener { dialog, id ->
                        // キャンセルボタンが押されたときの処理
                    })
        return builder.create()
    }
}

メソッドをチェインにして記述する

メソッドをチェインにして次のように記述することもできます

ちょっとすっきりしたかも

class SampleDialog : DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle): Dialog {
        return AlertDialog.Builder(requireContext())
            .setMessage("Sample Message")
            .setPositiveButton("OK",
                    DialogInterface.OnClickListener { dialog, id ->
                        // OKボタンが押されたときの処理
                    })
            .setNegativeButton("Cancel",
                    DialogInterface.OnClickListener { dialog, id ->
                        // キャンセルボタンが押されたときの処理
                    })
            .create()
    }
}

OnClickListener の部分を省略

OnClickListener の部分を省略して次のように記述することもできます

うん、すごくすっきり

class SampleDialog : DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle): Dialog {
        return AlertDialog.Builder(requireContext())
            .setMessage("Sample Message")
            .setPositiveButton("OK") { dialog, id ->
                // OKボタンが押されたときの処理
            }
            .setNegativeButton("Cancel") { dialog, id ->
                // キャンセルボタンが押されたときの処理
            }
            .create()
    }
}

ダイアログを表示する方法

表示はこれだけでできちゃうの

ダイアログ用のクラスのインスタンスを作成して show() メソッドで表示できます

SampleDialog().show(childFragmentManager,"tag")

ダイアログ(AlertDialog)で利用できる部品

ここにある部品だけしか使わないなら、XMLのレイアウトは作らなくていいんですね

ダイアログには以下の部品が最初から用意されています。これらについては XML でレイアウトを用意せずに利用できます

  • タイトル・・・setTitle("タイトル")
  • メッセージ・・・setMessage("メッセージ")
  • リスト・・・setItems(項目リスト) { dialog, which -> リストの処理 }
    • 項目リストは CharSequence 型の配列
    • 引数の which は選択された配列のインデックス
    • setAdapter() を使用して項目リストを指定することも可能。ListAdapter を使ってデータベースからリストを取得したりできます
  • ラジオボタンのリスト・・・setSingleChoiceItems(項目リスト, 初期選択項目) { dialog, which -> リストの処理 }
    • 項目リストは CharSequence 型の配列
    • 初期選択項目は Int 型。-1 指定で選択なし
    • 引数の which は選択された配列のインデックス
  • チェックボックスのリスト・・・setMultiChoiceItems(項目リスト, 初期選択項目) { dialog, which, isChecked -> リストの処理 }
    • 項目リストは CharSequence 型の配列
    • 初期選択項目は Boolean 型の配列。null 指定で選択なし
    • 引数の which は選択された配列のインデックス
  • アクションボタン(Positive, Negative,Neutral の3つまで)
    • setPositiveButton("OK") { dialog, id -> 処理 }
    • setNegativeButton("Cancel") { dialog, id -> 処理 }
    • setNeutralButton("後で") { dialog, id -> 処理 }

上記以外の部品を使いたいときは XML でカスタムレイアウトを作成して利用します

カスタムレイアウトの使用

ダイアログでも EditText とか NumberPicker みたいな部品を使ってみたいかも…

うん、そういうときはレイアウトファイルを自分で作っちゃえばいいの

カスタムレイアウトで NumberPicker を追加するには次のようにします

  • XML で NumberPicker を使ったレイアウトを作成します(dialog_sample.xml)
  • 以下のようにダイアログ用のクラスの中でレイアウトを inflate して NumberPicker の設定を行います
  • setView() メソッドを使って AlertDialog に設定します
class SampleDialog : DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle): Dialog {

        ...
        // カスタムレイアウトを生成
        val dialogView = requireActivity()
                 .layoutInflater
                 .inflate(R.layout.dialog_sample, null)!!
        // カスタムレイアウトの設定
        val np = dialogView.findViewById<NumberPicker>(R.id.numberPicker)
        np.minValue = 0
        np.maxValue = 9
        np.value = 0
        ...

        return AlertDialog.Builder(requireContext())
            .setView(dialogView)
            .setTitle("Sample Title")
            .setPositiveButton("OK") { dialog, id ->
                // OKボタンが押されたときの処理
            }
            .setNegativeButton("Cancel") { dialog, id ->
                // キャンセルボタンが押されたときの処理
                dialog.cancel()
            }
            .create()
    }
}

ダイアログを終了させる

終了前の処理も忘れないでくださいね

ダイアログを閉じる

  • ダイアログが閉じるタイミング
    • アクションボタンをタップしたとき
    • ラジオボタン、チェックボックス以外のアイテムをタップしたとき
    • dismiss() が呼び出されたとき
  • ダイアログが閉じるときに発生するコールバック

ダイアログをキャンセルする

キャンセルは、タスクを終了せずにダイアログを離れることです

  • ダイアログがキャンセルされるとき
    • 戻るボタンを押したとき
    • ダイアログのエリア以外のところをタップしたとき
    • cancel() が呼び出されたとき。たとえば getDialog()?cancel() として呼び出します
    • onCancel(dialog) でコールバックを呼び出してからダイアログを閉じる(必要に応じて dismiss() を呼び出す)ことでキャンセルすることもできます
  • ダイアログがキャンセルされたときに発生するコールバック

ポイント

  • キャンセルの動作で onCancel() が呼び出されるときは onDismiss() も呼び出されます
  • dismiss() の呼び出しでは onDismiss() が呼ばれますが onCancel() は呼ばれません

その他

ほかに、こんな処理もできるの

ダイアログのホストにイベントを渡す

  • ダイアログ用のクラスの中でイベントを渡すためのインタフェースを作成して、onAttach() メソッドをオーバーライドします
  • ホスト側でインタフェースを実装して利用します

ダイアログを通常のフラグメントとして利用する

  • ダイアログを状況に応じて通常のフラグメントとして利用することも可能です
  • この場合、AlertDialog.Builder などを使ったダイアログ作成ではなく、レイアウトでUI作成をして onCreateView() で読み込むようにします