うきっとラボ~中卒から始めるプログラミング~

中卒のポンコツ太郎が立派なプログラマになるまでの道のり

【Android】~ちょこメモ~ダイアログ「画面外タップで閉じる」操作を無効化する

はじめに

今回の「ちょこっとメモ」、

自分の語彙力だとタイトルがどうしても長くなってしまうので

断腸の思いで「ちょこメモ」に改名しました…(´;ω;`)笑



ダイアログの実装方法について知りたい方は、
こちらの記事をご覧ください
ukit-labo.hateblo.jp



調べたきっかけは、今現在開発中の個人アプリで

「”OK”か”NG”か、必ずどちらかを選択するダイアログ」

を作成したい場面が出てきた時でした。

まずは、場面を想像しやすいように
サンプルコードを紹介します。



サンプルコード

今回題材として使用するサンプルコードです。

activity_main.xml

まずは、アクティビティです。
ボタンのみです。YES。

<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="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onDialogButtonClick"
        android:text="@string/btn_dialog"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

f:id:ukiuki0518:20200123190913p:plain:h400
中央に、押すとダイアログを表示するボタンを配置

SampleDialogFragment.kt

次に、「ダイアログ」を表すクラスです。

どちらのボタンが押されたかをトーストで表示する、という
極限までシンプルなダイアログにしました。

class SampleDialogFragment: DialogFragment() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

        val builder = AlertDialog.Builder(activity)
        builder.setTitle("サンプル")
        builder.setPositiveButton("OK"){_, _ ->
            Toast.makeText(context, "OKボタンが押されました", Toast.LENGTH_LONG).show()
        }
        builder.setNegativeButton("NG"){_, _ ->
            Toast.makeText(context, "NGボタンが押されました", Toast.LENGTH_LONG).show()
        }
        return builder.create()
    }
}

f:id:ukiuki0518:20200123191737p:plain:h400
選択肢はOKとNGだけ

MainActivity.kt

最後にアクティビティクラスです。

ボタンを押すとダイアログを表示するプログラム
だけですね笑

超シンプル。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun onDialogButtonClick(view: View){
        val dialog = SampleDialogFragment()
        dialog.show(supportFragmentManager, "SampleDialogFragment")
    }
}



問題点

私がこのダイアログに求める挙動は
「”OK”か”NG”、どちらかを必ず選択する」、

要するに
キャンセル操作を無効化する

というものでした。

しかし現在(デフォルト)の挙動では、
ダイアログ画面外をタップすると、ダイアログが閉じてしまいます

f:id:ukiuki0518:20200123185655g:plain:h400
ダイアログ画面外をタップすると閉じてしまう

これは、ダイアログ生成時にsetNeutralButton()をして
キャンセルボタン」を作っていなくても、
キャンセルは可能

ということになります。

これじゃあ思った通りの実装ができないめぅ(;_;)
ってことです(?)


解決策

ダイアログフラグメントに対して
「isCancelable() = false」とします。

isCancelable()メソッド
ダイアログのキャンセル操作の有効・無効」をboolean値で設定します。


①ダイアログクラスに記述するパターン

class SampleDialogFragment: DialogFragment() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

        val builder = AlertDialog.Builder(activity)
        builder.setTitle("サンプル")
        builder.setPositiveButton("OK"){_, _ ->
            Toast.makeText(context, "OKボタンが押されました", Toast.LENGTH_LONG).show()
        }
        builder.setNegativeButton("NG"){_, _ ->
            Toast.makeText(context, "NGボタンが押されました", Toast.LENGTH_LONG).show()
        }

        //↓↓これを追加する。
        this.isCancelable = false

        return builder.create()
    }
}

もしくは、

②ダイアログを呼び出した側に記述するパターン

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun onDialogButtonClick(view: View){
        val dialog = SampleDialogFragment()

        //↓↓これを追加する
        dialog.isCancelable = false

        dialog.show(supportFragmentManager, "SampleDialogFragment")
    }
}

f:id:ukiuki0518:20200123202919g:plain:h400
画面外タップが無効になった


ポイント

まず
①ダイアログクラスに追加するパターン」について

記述場所は

  • onCreateDialog()の中
  • return の前

そして、

  • 「this」に対して

isCalcelable()を呼び出していることがポイントです。

この場合の「this」は”DialogFragment”であり、
生成する"AlertDialog"または"AlertDialog.Builder"ではありません。


次に
②ダイアログを呼び出した側に記述するパターン」です。

生成したSampleDialogFragmentのインスタンスに対して、isCalcelable()を設定しています。

”SampleDialogFragment”は”DialogFragment”を継承しているため、
タイミングが違うだけで
やっていることは①と同じですね。


使い分けとしては、

ダイアログクラスから同じダイアログしか生成しない場合
①のパターン。

ダイアログクラスを使いまわして使用する場合キャンセル操作を個々で設定したい場合などは
②のパターン。


ってな感じで使えばいいんじゃないでしょうか。

注意点

この操作では、
生成したダイアログのすべてのキャンセル操作を無効にします。

つまり、
”戻るボタン”によるキャンセル操作」
無効になってしますの注意しましょう!