AlertDialogのカスタマイズその2：戻り値を受け取ろう

[前の記事](https://qiita.com/kame_yang/items/67978aa3cde688443cf5)では`AlertDialog`に引数を渡す方法を紹介しました。
今回は逆に`AlertDialog`から戻り値を受け取る方法について触れてみたいと思います。


# 作成するサンプル
Dialog上に`EditText`を配置し、そこで入力された値を`MainActivity`で受け取り、`TextView`に表示するアプリを作ります。
![アプリ.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1073313/13e3b6af-788c-bab9-5ff3-f455c489fee8.gif)


# 実装方法
それでは実装方法を見てみましょう。

## MaterialAlertDialogBuilder()を使う（コードで実装）

`Dialog`を表示するボタンがクリックされた時のサンプルは以下の通りです。
ここでは`コンテンツエリア`に表示するレイアウトは全てコードで実装します。

#### MainActivity.kt

```kotlin
class MainActivity : AppCompatActivity() {

    // （省略）

    private fun onButtonClick(view: View){
        ////// Dialogのコンテンツエリアに設定するレイアウトを作成 ////////////
        // ConstraintLayout生成
        val constraintLayout = ConstraintLayout(this@MainActivity)
        // レイアウトパラメーターの設定
        constraintLayout.layoutParams = ConstraintLayout.LayoutParams(
            ConstraintLayout.LayoutParams.MATCH_PARENT,
            ConstraintLayout.LayoutParams.WRAP_CONTENT
        )
        // IDの生成
        constraintLayout.id = View.generateViewId()
        // ConstraintLayoutの生成
        val constraintSet = ConstraintSet()
        constraintSet.clone(constraintLayout)

        // EditTextのレイアウトを作成
        val textInputLayout = TextInputLayout(this@MainActivity)
        // 表示形式の設定
        textInputLayout.boxBackgroundMode = TextInputLayout.BOX_BACKGROUND_OUTLINE
        // レイアウトパラメーターの設定
        textInputLayout.layoutParams = ConstraintLayout.LayoutParams(
            ConstraintLayout.LayoutParams.MATCH_PARENT,
            ConstraintLayout.LayoutParams.WRAP_CONTENT
        )
        // IDの生成
        textInputLayout.id = View.generateViewId()

        // EditTextの生成
        val textInputEditText = TextInputEditText(this@MainActivity)
        // IDの生成
        textInputEditText.id = View.generateViewId()

        // EditTextをEditTextレイアウトに追加
        textInputLayout.addView(textInputEditText)
        // EditTextレイアウトをConstraintLayoutに追加
        constraintLayout.addView(textInputLayout)
        /////////////////////////////////////////////////////////////


        // Builderオブジェクトの生成
        val builder = MaterialAlertDialogBuilder(this@MainActivity)
        // Dialogのタイトルを設定
        builder.setTitle("文字列を入力してください。")

        // 作成したConstraintLayoutをDialogのコンテンツエリアに追加
        builder.setView(constraintLayout)

        // Positiveボタンの処理を記述
        builder.setPositiveButton("OK"){
            // TextViewにEditTextで入力された値を直接設定
            _,
            _ -> findViewById<TextView>(R.id.tvDisplay).text = textInputEditText.text.toString()
        }
        // ダイアログの表示
        val dialog = builder.create()
        dialog.show()
    }
}
```

「Dialogのコンテンツエリアに設定するレイアウトを作成」とコメントで囲ってある部分が`Dialog`の`コンテンツエリア`に`EditText`を表示させるための処理です。

元となる`ConstraintLayout`を生成し、その中に`TextInputLayout`、`TextInputEditText`を順々に詰めていき、作成した`ConstraintLayout`を`setView()`で`Dialog`に設定してあげるイメージでしょうか？

もちろん`LinearLayout`でも実装はできますし、`Button`や`TextView`、画像を設定することもできます。

## MaterialAlertDialogBuilder()を使う（レイアウトファイルを使用）

上記のやり方ではレイアウトの作成まで全てコードで表現しましたが、もちろんレイアウトファイル（.xml）を使って実装することも可能です。

#### MainActivity.kt

```kotlin
class MainActivity : AppCompatActivity() {

    // （省略）

    private fun onButtonClick(view: View){
        // Builderオブジェクトの生成
        val builder = MaterialAlertDialogBuilder(this@MainActivity)
        // Dialogのタイトルを設定
        builder.setTitle("文字列を入力してください。")

        ////// Dialogのコンテンツエリアに設定するレイアウトを作成 ////////////
        // Contentレイアウトの作成
        val contentLayout = layoutInflater.inflate(R.layout.dialog_customize, null)
        // EditTextのViewを取得しておく
        val etInput = contentLayout.findViewById<EditText>(R.id.etInput)
        /////////////////////////////////////////////////////////////

        // 作成したContentLayoutをDialogのコンテンツエリアに追加
        builder.setView(contentLayout)

        // Positiveボタンの処理を記述
        builder.setPositiveButton("OK"){
            // TextViewにEditTextで入力された値を直接設定
                _,
                _ -> findViewById<TextView>(R.id.tvDisplay).text = etInput.text.toString()
        }
        // ダイアログの表示
        val dialog = builder.create()
        dialog.show()
    }
}
```

だいぶスリムになりましたね。
`コンテンツリア`に表示するレイアウトは`inlater`から生成しています。
`inflate()`メソッドは`ID`のほか、親となる`View`を引数として与える必要がありますが、ここではnullで問題ありません。

#### dialog_customize.xml

```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/etInput"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>
```

レイアウトファイルは非常にシンプルに、`EditText`を実装しているだけです。
もちろん他の画面部品を設定することもできるので、`AlertDialog`といえど表現の幅はとても広いです。

## DialogFragmentを使用する

参考までに`DialogFragment`クラスを生成し、そこからレイアウトを設定するコードも載せておきます。

#### CustomDialogFragment.kt

```kotlin
class CustomDialogFragment : DialogFragment() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let{
            // Builderクラスを生成
            val builder = AlertDialog.Builder(it)
            // Dialogのタイトルを設定
            builder.setTitle("文字列を入力してください。")
            // inflaterを取得
            val inflater = requireActivity().layoutInflater
            // Viewを設定
            builder.setView(inflater.inflate(R.layout.dialog_customize, null))
            // AlertDialogオブジェクトを生成
            builder.create()
        } ?: throw IllegalStateException("Activity cannot be null")
    }
}
```

`inflate`のやり方が上記のものとは少し違う点に注意してください。


# 参考文献
「**MaterialAlertDialogBuilder()を使う（コードで実装）** 」は[900+ Android Code Snippets](https://android--code.blogspot.com/)さんの記事「android kotlin - AlertDialog EditText programmatically」（ https://android--code.blogspot.com/2020/03/android-kotlin-alertdialog-edittext.html ）を参考にサンプルコードを実装させていただきました。

また「**DialogFragmentを使用する**」の方は公式ドキュメント（ https://developer.android.com/guide/topics/ui/dialogs?hl=ja#PassingEvents ）を参考にしています。