DialogFragmentを使ったダイアログの表示 (Android Kotlin)

/





Androidの公式ドキュメントよりダイアログの表示サンプルを試してみた。ドキュメントではJavaで記述されているがここではKotlinに書き直して実装した。

なお、本プロジェクトはGithubリポジトリへ公開してある。





ダイアログの基本


Androidでダイアログを表示させるにはDialogクラスを継承したAlertDialogを使う。Activityでインスタンス生成するのではなく、DialogFragmentクラスを経由してインスタンスを生成する。DialogFragmentクラスはFragmentを継承しているのでActivityのライフサイクルと同期することができる。また、DialogFragmentでカプセル化されるのでActivityが肥大化せず、ダイアログを管理しやすくなる。

実際にサンプルを見たほうが分かりやすいので、次ではCancelDoneの2つのボタンを実装したシンプルなダイアログを作っていく。


シンプルなダイアログ




DialogFragmentクラスを継承したSimpleDialogFragmentクラスを作成する。onCreateDialogメソッドをオーバーライドしてその中でAlertDialogのインスタンスをビルダーで生成している。



Activityからは次のようにして呼び出すことができる。以降、ダイアログの呼び出しはすべてこの方法で行う。




リストメニューのダイアログ




先程のシンプルなダイアログではsetPositiveButtonなどでボタンを追加したが、リスト表示ではsetItemsメソッドを使って配列をセットする。ここではsetMessageを実装してしまうとリストが表示されなくなるので注意したい。



setItemsメソッドの第二引数へはクリックされたときの処理をラムダ式を渡すことになっている。このラムダ式はOnClickListenerインタフェースで定義されている。ラムダ式内の第二引数(which)にはクリックされた配列のインデックスが渡される。




チェックボックスのダイアログ




チェックボックスタイプのダイアログにはsetMultiChoiceItemsを使う。setMultiChoiceItemsの第二引数には、予めチェックしておきたい場所をbooleanArray型で渡すことができる。





ラジオボタンのダイアログ




ラジオボタンタイプのダイアログではsetSingleChoiceItemsを使う。setSingleChoiceItemsの第二引数にデフォルトでチェックしておきたいインデックスをint型で渡すことができる。




カスタムレイアウトのダイアログ(ログイン入力)




ダイアログのレイアウトを自由にカスタマイズすることができる。ここでは公式ドキュメントにならってログイン入力用のダイアログを実装した。

まずは表示したいレイアウトファイルdialog_signin.xmlを次のように作成する。




そしてこのレイアウトをDialogFragmentクラス内でinflaterinflateする。つまりdialog_signinレイアウトを元にしたViewを生成する。丁寧にプログラムを追えば何も難しいことはない。




ダイアログからアクティビティへコールバックする




最後にアクティビティへコールバック可能なダイアログを作成する。インタフェースを使ってコールバックを実装することでActivityとダイアログを疎結合にすることができ、キレイなコードで管理することができる。ここではシンプルなダイアログのプログラムを元に改造した。




上記のプログラムを詳しく見ていこう。

まず、コールバック用のインターフェースNoticeDialogListerを、DialogFragmentを継承したNoticeDialogFragmentクラス内に作成する。





このインタフェースはダイアログを呼び出すホストつまりMainActivityへ実装することになる。



onAttachMainActivity(ここではContextになっているが)をNoticeDialogListerへキャストしている。ホストのActivityNoticeDialogListerインタフェースが実装されていなければ例外がスローされるようになっている。このことで実装のし忘れを避けることができるのだ。



そしてダイアログのボタンクリックイベント内でNoticeDialogListerのメソッドを実行しているのがわかるだろう。



このコールバックの実装パターンは、iOS開発では頻繁に行われるデリゲートでのコールバックとよく似ている。Objective-C/SwiftのprotocolがJava/Kotlinのinterfaceに対応しているのだ。またiOSでは強参照を避けるためにweakを指定するが、AndroidでのFragmentを通した実装ではその必要がないようである。ここは確かなことは言えないのだが、ダイアログを終了するとDialogFragmentonDestroyonDetachが呼ばれることを確認しているので問題ないということにしたい。


英語雑学




Dialog / Dialogueの語源はlog(話す)から来ている。logはギリシャ語で「話す」や「単語」「会話」を意味する。dia(間で) + log(話す) であるのでつまりは「対話」「意見交換」といった意味になる。英英辞書では(a) written conversation in a book or playと書かれているように、本やゲームにおいての書かれた対話となる。

またlogにはlog houseというようにという意味もある。文字を木に書いていた歴史からもlogが対話(書かれた)などの意味をもつのは自然なことではないだろうか。


* The mayor tried to maintain a dialogue with the citizens.
* He's not very good at writing dialogue.


ちなみにlogを語源とする単語は他にもたくさん存在する。

* catalog(カタログ)
* logo(ロゴ)
* logic(論理)
* apology(謝罪)
* prologue(序幕)
* epilogue(結びの言葉)
* monologue(独白)

アプリのDialogもユーザーとの対話ということなので、一方的なスパムアラートにならないように表示のやり方には十分気を配りたいところである。


参考


* ダイアログ (Android Developers/ドキュメント/ガイド)
* 語根で覚える英単語 プラス ――語源によるサクサク英単語10倍記憶法




関連記事


非同期 & ネットワーク処理
* はじめてRetrofitを使ってみる (Android Kotlin)
* 非同期処理でCoroutine(コルーチン)を使ってみる (Android Kotlin)
* OkHttpでHTTP通信 (Android Kotlin)
* スレッド処理 (Android Kotlin)

View & レイアウト
* リサイクラービューを使ってリスト表示 (Android Kotlin)
* 角丸表現 (Android Kotlin)
* onSaveInstanceStateで画面回転時の状態の保存と復元 (Android Kotlin)
* ListViewを簡単なモデルで理解しよう (Android Kotlin)
* バネアニメーション (Android Kotlin)
* startActivityForResultを使ってみる (Android Kotlin)
* ActivityとFragmentの連携を理解する (Android Kotlin)
* DataBinding機能を使ってみる (Android Kotlin)
* タッチイベントのフック (Android Kotlin)
* DialogFragmentを使ったダイアログの表示 (Android Kotlin)

サウンド
* これってSoundPoolのバグ?(Android Kotlin)
* SoundPoolを使ってゲームの効果音を再生する (Android Kotlin)

データ & オブジェクト
* Kotlinでオブジェクト(object)の動作確認
*
データクラスとタプル (Android Kotlin)
* Null安全、ぬるぽバイバイ (Android Kotlin)