【Kotlin/Android】DialogFragmentの使い方!独自レイアウトのカスタムダイアログ実装

この記事からわかること

  • Android Studio/KotlinDialogFragment使い方
  • アプリカスタムダイアログ実装する方法
  • 独自レイアウトで作成するには?
  • データ処理渡す方法

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

以下のようなOS標準のアラートではなく、独自のレイアウトを使用したカスタムダイアログを実装する方法をまとめていきます。

【Kotlin/Android Studio】AlertDialogの使い方!DialogFragmentでカスタムダイアログ

カスタムダイアログ:DialogFragment

独自のレイアウトを使用したカスタムダイアログを実装するためにはDialogFragmentを使用してFragment側にダイアログの処理を記述します。まずはDialogFragmentの利用方法を理解するためにOS標準のダイアログ処理を実装してみます。

実装の手順

  1. DialogFragmentを継承したFragmentクラスの作成
  2. onCreateDialogメソッドをオーバーライドしてダイアログ構築処理を実装
  3. builder.create()してDialogインスタンスを取得しreturn

import android.app.Dialog
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment

class CustomDialogFragment: DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val builder = AlertDialog.Builder(this.requireContext())
        builder.setTitle("ここにタイトル")
            .setMessage("ダイアログのメッセージ")
            .setPositiveButton("OK", { dialog, id ->
                // ボタンクリック時の処理
            })
            .show()

        return builder.create()
    }
}

ダイアログ表示処理をFragmentに切り出したことで流用しやすくなりまた、「MainActivity.kt」側などから以下のようにすっきりと呼び出すことができるようになります。


val button:Button = findViewById(R.id.done_button)
button.setOnClickListener{
    val dialog = CustomDialogFragment()
    dialog.show(supportFragmentManager, "custom")
}
【Kotlin/Android Studio】AlertDialogの使い方!DialogFragmentでカスタムダイアログ

独自のレイアウトダイアログ(xml)を表示する

ダイアログとして表示するビューを独自にレイアウトしたものを実装する方法を紹介していきます。この方法は専用のレイアウトファイル(xml)を用意して表示させる流れになります。

実装の手順

  1. DialogFragmentを継承したFragmentクラスの作成
  2. 独自レイアウトのXMLファイルを準備
  3. onCreateDialogメソッドをオーバーライドしてダイアログ構築処理を実装
  4. builder.create()してDialogインスタンスを取得しreturn

1.DialogFragmentを継承したFragmentクラスの作成

まずはDialogFragmentを継承したFragmentクラスを新規で作成しておきます。関連したレイアウトファイルも一緒に新規作成しておけばOKです。


class CustomNotifyDialogFragment: DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

    }
}

2.独自レイアウトのXMLファイルを準備

自動追加されたレイアウトファイルの中身を編集し、表示させたい独自レイアウトを実装ていきます。


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="300dp"
    android:layout_height="220dp"
    android:background="@color/ex_thema">

    <TextView
        android:id="@+id/dialog_title"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/ex_red"
        android:padding="12dp"
        android:text="@string/dialog_title_notice"
        android:textAlignment="center"
        android:textColor="@color/white"
        android:textSize="16dp"
        app:layout_constraintBottom_toTopOf="@+id/dialog_msg"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0" />

    <TextView
        android:id="@+id/dialog_msg"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="messagemessagemessagemessagemessagemessagemessage"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/dialog_title" />


    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_begin="172dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/guideline2">


        <Space
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1" />

        <Button
            android:id="@+id/negative_button"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:text="NG" />

        <Button
            android:id="@+id/positive_button"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:text="OK" />

        <Space
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

3.onCreateDialogメソッドをオーバーライドしてダイアログ構築処理を実装

作成したカスタムダイアログクラスのonCreateDialogメソッド内でレイアウトファイルを参照してビューを構築していきます。

fragment_custom_notify_dialogレイアウトをインフレートしsetViewメソッドでAlertDialog.Builderインスタンスに渡します。またレイアウトの中のビューにはinflateで取得したViewから参照することが可能です。


override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

    val builder = AlertDialog.Builder(this.requireContext())
    val inflater = this.requireActivity().layoutInflater
    val dialog = inflater.inflate(R.layout.fragment_custom_notify_dialog,null)

    val button: Button = dialog.findViewById(R.id.positive_button)
    val title: TextView = dialog.findViewById(R.id.dialog_title)
    title.text = "タイトル"
    val msg: TextView = dialog.findViewById(R.id.dialog_msg)
    msg.text = "メッセージ"
    button.setOnClickListener {
        dismiss()
    }
    builder.setView(dialog)
    return builder.create()
}

DialogFragmentのライフサイクルはonCreateDialogが呼ばれonViewCreatedなどは呼ばれないので注意してください。

最後にActivityクラスなどからカスタムダイアログクラスをインスタンス化しshowメソッドを呼び出すことで実装できます。


 val button:Button = findViewById(R.id.done_button)
button.setOnClickListener{
    val dialog = CustomNotifyDialogFragment()
    dialog.show(supportFragmentManager, "custom")
}
【Kotlin/Android Studio】AlertDialogの使い方!DialogFragmentでカスタムダイアログ

Activityなどからデータを渡して表示する

ダイアログに表示させたいデータは他のActivityなどから受け取ったデータにしたいことのが多いと思います。外部から受け取ったデータを反映させたい場合はFragmentへのデータ渡しの方法と同じでコンストラクタの引数ではクラッシュしてしまうのでBundleを介してデータを渡します。詳細な実装方法はFragment側の以下の記事を参考にしてください。

実装の流れ

  1. キーを準備
  2. データ格納用のプロパティを用意
  3. コンストラクタではないインスタンス生成用メソッドの準備
  4. onCreate内でBundleからデータを取得してプロパティにセット

// ⓵キーを準備
public const val ARG_DIALOG_TITLE_KEY = "ARG_DIALOG_TITLE_KEY"
public const val ARG_DIALOG_MSG_KEY = "ARG_DIALOG_MSG_KEY"
  
class CustomNotifyDialogFragment : DialogFragment() {

    // ⓶データ格納用のプロパティを用意
    private var title: String = ""
    private var msg: String = ""

    // ⓷コンストラクタではないインスタンス生成用メソッドの準備
    // ここでデータを外部から引数で受け取りBundleに保存する
    companion object {
        @JvmStatic
        public fun newInstance(title: String, msg: String) =
            CustomNotifyDialogFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_DIALOG_TITLE_KEY, title)
                    putString(ARG_DIALOG_MSG_KEY, msg)
                }
            }
    }

    // ⓸onCreate内でBundleからデータを取得してプロパティにセット
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            title = it.getString(ARG_DIALOG_TITLE_KEY).toString()
            msg = it.getString(ARG_DIALOG_MSG_KEY).toString()
        }
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

        val builder = AlertDialog.Builder(this.requireContext())
        val inflater = this.requireActivity().layoutInflater
        val dialog = inflater.inflate(R.layout.fragment_custom_notify_dialog,null)

        val button: Button = dialog.findViewById(R.id.positive_button)
        val title: TextView = dialog.findViewById(R.id.dialog_title)
        title.text = this.title
        val msg: TextView = dialog.findViewById(R.id.dialog_msg)
        msg.text = this.msg
        button.setOnClickListener {

            dismiss()
        }
        builder.setView(dialog)
        return builder.create()
    }
}

ダイアログを表示させる際はnewInstanceメソッドでインスタンスを生成して使用します。


 val button:Button = findViewById(R.id.done_button)
button.setOnClickListener{
  val dialog = CustomNotifyDialogFragment.newInstance("お知らせ",  "メッセージ")
  dialog.show(parentFragmentManager, "custom")
}

Activityなどから処理を渡して実行する

ダイアログのボタン押下時の処理を外部から渡せるようにしておけば、汎用的なカスタムダイアログクラスになり流用しやすくなります。外部から処理を渡すためにはDialogとActivityに依存関係が生じないように注意を払う必要があります。そのためDialog⇄ActivityとならないようにDialog⇄listener⇄Activityとすることで依存し合わないようにしています。

以下の記事でFragment⇄listener⇄Activityのやり取りを解説しているので参考にしてください。

実装の流れ

  1. リスナー本体を定義
  2. リスナーをlateinitプロパティとして定義
  3. リスナープロパティにセットするメソッドを用意
  4. 外部から渡された処理を実行したい箇所で呼び出す

class CustomNotifyDialogFragment : DialogFragment() {

    // 〜〜〜〜 省略 〜〜〜〜

    // ⓶リスナーをlateinitプロパティとして定義
    private lateinit var listner: onTappedListner

    // ⓵リスナー本体を定義
    interface onTappedListner {
        fun onTapped()
    }

    // ⓷リスナープロパティにセットするメソッドを用意
    // これは外部から呼び出す
    public fun setOnTappedListner(listener: onTappedListner) {
        this.listner = listener
    }

     // 〜〜〜〜 省略 〜〜〜〜

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

        // 〜〜〜〜 省略 〜〜〜〜
        
        button.setOnClickListener {
            // ⓸外部から渡された処理を実行したい箇所で呼び出す
            listner.onTapped()
            dismiss()
        }
        builder.setView(dialog)
        return builder.create()
    }
}

Activityから処理を渡すには定義したsetOnTappedListnerメソッドを呼び出し、引数にタップ時に実行したい処理を組み込んだonTappedListner型を渡せばOKです。


val button:Button = findViewById(R.id.done_button)
button.setOnClickListener{
  val dialog = CustomNotifyDialogFragment.newInstance("お知らせ",  "メッセージ")
  dialog.setOnTappedListner(
      object :CustomNotifyDialogFragment.onTappedListner{
          override fun onTapped() {
              Log.e("------","タップしたよ")
          }
      }
  )
  dialog.show(parentFragmentManager, "custom")
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index