ダイアログとは通常、現在のアクティビティの前面に現れる小さなウィンドのことを指します。背面のアクティビティはフォーカスを失い、ダイアログがユーザのすべてインタラクションを受け付けます。ダイアログは普通ユーザ通知やアプリケーションが処理中に直接関わる短時間のアクティビティとして使用されます。
Android API は以下のタイプの Dialog オブジェクトをサポートしています。
- AlertDialog
- ボタンなし、1個、2個、または、3個のボタンかつ(または)チェックボックスまたはラジオボタンを含む選択可能なリストを管理することができます。AlertDialog はほとんどのダイアログインターフェイスを使用できる提案型のダイアログです。 下記の アラートダイアログの作成 を参照。
- ProgressDialog
- プログレスホイールやプログレスバーを表示するダイアログです。 AlertDialog の拡張なのでボタンもサポートしています。下の プログレスダイアログの作成 を参照。
- DatePickerDialog
- ユーザが日付を選択できるダイアログです。Hello DatePicker チュートリアルを参照。
- TimePickerDialog
- ユーザが時刻を選択できるダイアログです。Hello TimePicker チュートリアルを参照。
独自にダイアログをカスタマイズしたい場合は、ベースの Dialog オブジェクトまたは上記でリストしたサブクラスを拡張し、新たなレイアウトを定義することが可能です。下記のセクションの カスタムダイアログの作成 を参照してください。
ダイアログの表示
ダイアログは常に Activity の部品として作成され表示されます。通常はアクティビティ内の onCreateDialog(int) コールバックメソッドからダイアログを生成します。このコールバックを使用すると、Android システムが各ダイアログの状態を自動的に管理し、それらをアクティビティと関連付けを行うことによりそのアクティビティがダイアログの事実上の"オーナー"となります。そのようにして各ダイアログがアクティビティから一部のプロパティが継承されます。例えば、ダイアログがオープンされ、そのメニューキーでアクティビティで定義されたオプションメニューが展開され、そのボリュームキーでアクティビティで使用されているオーディオストリームが変更されます。
注意: onCreateDialog() メソッドの外部でダイアログを作成してしまうと、アクティビティにアタッチされません。しかしながら、 setOwnerActivity(Activity) でアクティビティにアタッチすることができます。
ダイアログを表示したい場合は、 showDialog(int) を呼び出し、表示するダイアログを識別するユニークな数値を引数に与えます。
初めてダイアログが要求されると、Android がアクティビティから onCreateDialog(int) (ここが Dialog をインスタンス化すべき場所になります)を呼び出し、このコールバックメソッドに showDialog(int) の引数に与えた ID が渡されてきます。ダイアログを作成後、このメソッドの最後でオブジェクトが返されます。
また、Android はダイアログが表示される前に onPrepareDialog(int, Dialog) というオプションのコーバックメソッドを呼び出します。ダイアログがオープンされるたびにダイアログの何らかのプロパティを変更したい場合はこのメソッドを定義してください。このメソッドはダイアログがオープンされるたびに呼び出されますが、 onCreateDialog(int) が呼び出されるのはダイアログがオープンされる最初の1回のみです。 onPrepareDialog() を定義していない場合は、ダイアログは前回オープンしたときと同じ状態のままになります。このメソッドも onCreateDialog() で生成されたダイアログオブジェクトと一緒にダイアログの ID が渡されます。
onCreateDialog(int) と onPrepareDialog(int, Dialog) コールバックメソッドの定義内では、このメソッドに渡される id パラメータを判定する switch 文を使用するのがもっとも良い方法です。各 case 文ではダイアログのユニークな ID を判定し、それぞれに対応するダイアログを生成し定義することになります。例えば、ゲームではふたつの異なるダイアログ(ひとつはゲームが一時停止している、もうひとつはゲームオーバーになったことを知らせる)が使われるであろうことが想像できるでしょう。ではまず以下のように、各ダイアログの数値 ID を定義します。
static final int DIALOG_PAUSED_ID = 0;
static final int DIALOG_GAMEOVER_ID = 1;
次に、以下のように onCreateDialog(int) コールバックを定義し、各 ID に対する分岐処理を記述します。
protected Dialog onCreateDialog(int id) {
Dialog dialog;
switch(id) {
case DIALOG_PAUSED_ID:
// do the work to define the pause Dialog
break;
case DIALOG_GAMEOVER_ID:
// do the work to define the game over Dialog
break;
default:
dialog = null;
}
return dialog;
}
注意: このサンプルでは、ダイアログを定義する処理はこのセクションの範囲外という理由から、case 文の内部のコードは記述していません。下のセクションの アラートダイアログの作成 に関する説明を参照してください。そこにこのサンプルに対する適切なコードが記述してあります。
ダイアログを表示するタイミングで、以下のようにダイアログのIDを指定して showDialog(int) を呼び出します。
showDialog(DIALOG_PAUSED_ID);
ダイアログの破棄
ダイアログをクローズする準備ができたときは、ダイアログオブジェクトに対し dismiss() を呼び出すことにより破棄することができます。必要であればアクティビティから dismissDialog(int) を呼び出す(実際はダイアログの dismiss() を呼び出しています) ことも可能です。
ダイアログの状態を管理するために onCreateDialog(int) を使用した場合は(前のセクションで説明したように)、ダイアログが破棄されるたびに、ダイアログの状態がアクティビティにより記憶されます。このオブジェクトが以降不要であると判断した場合や、この状態をクリアすることが重要である場合には、 removeDialog(int) を呼び出すべきです。それによりこのオブジェクトに対する内部的な参照が破棄され、ダイアログが表示されている場合はそれが破棄されます。
破棄リスナの使用
ダイアログが破棄された時点でなんらかの処理を実行するようなアプリケーションがあると思いますが、その場合は、ダイアログにon-dismiss リスナをアタッチする必要があります。
まずはじめに DialogInterface.OnDismissListener インターフェイスを定義します。このインターフェイスはonDismiss(DialogInterface) というメソッドがひとつだけあり、このメソッドが呼び出されるのはダイアログが破棄されたときです。setOnDismissListener() で簡単に OnDismissListener の実装を与えることができます。
しかしながら、 ダイアログでは"キャンセル"できるということにも注意してください。これは、ダイアログがユーザにより明示的にキャンセルされるという特別なケースです。このケースは、ユーザがダイアログをクローズするために "back" ボタンを押したり、ダイアログが明示的に cancel() を呼び出したとき(おそらくダイアログの "Cancel" ボタンを押したことがきっかけとなって) に発生します。ダイアログがキャンセルされると、OnDismissListener により通知はされますが、そのダイアログが明示的にキャンセルされたということを知らせてほしい (かつ普通に破棄されていない)場合には、 DialogInterface.OnCancelListener を setOnCancelListener() と一緒に登録する必要があります。
アラートダイアログの作成
AlertDialog は Dialog クラスの拡張です。これはほとんどのダイアログユーザインターファイルを構成することができる提案型のダイアログです。このダイアログには以下のパーツを使用して構成することになるでしょう。
- タイトル
- テキストメッセージ
- 1個~3個のボタン
- 選択可能なアイテム (オプションチェックボックスやラジオボタン)
アラートダイアログの作成には、 AlertDialog.Builder サブクラスを使用します。 AlertDialog.Builder(Context) でビルダを取得し、そのクラスの public メソッドを使用してアラートダイアログのすべてのプロパティを定義します。ビルダに対してその処理を行った後、create()でアラートダイアログのオブジェクトを取得します。
AlertDialog.Builder クラスを使用し、さまざまなアラートダイアログのプロパティを定義する方法を以下のトピックで紹介します。 onCreateDialog() コールバックメソッドの内部で以下のサンプルコードを使用することにより、生成された結果が表示すべきダイアログの Dialog オブジェクトとなり、それを返却することができるようになります。
ボタンの追加
右のスクリーンショットにあるような横並びのボタンがあるアラートダイアログを作成するには、以下のようにset...Button() メソッドを使います。
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Are you sure you want to exit?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
MyActivity.this.finish();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
まず、 setMessage(CharSequence) でダイアログにメッセージを追加します。次に、メソッドの呼び出しの連結が始まり、 setCancelable(boolean) でそのダイアログをキャンセル不可 (そうすることでユーザがbackボタンでダイアログをクローズできなくなります) にします。それぞれのボタンで、setPositiveButton() のような set...Button() メソッドのいずれかを使用し、それにボタンの名前と、ユーザがボタンを選択したときのアクションを受け取ることを定義するための DialogInterface.OnClickListener を与えます。
注意: アラートダイアログに指定できるボタンのタイプはそれぞれひとつのみです。つまり、2個以上の "positive" ボタンは設定できません。また、使用可能のボタンの数は positive、 neutral、 negative の3つに制限しています。これらの名前はボタンの実際の機能とは関連はありませんが、どれがどのボタンかを追跡できるようにしておくべきでしょう。
リストの追加
右のスクリーンショットにあるような選択可能なリストがあるアラートダイアログを作成するには、以下のように setItems() メソッドを使います。
final CharSequence[] items = {"Red", "Green", "Blue"};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Pick a color");
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();
}
});
AlertDialog alert = builder.create();
まず、setTitle(CharSequence) でダイアログのタイトルを追加します。 setItems(), で選択可能なアイテムのリストを追加し、それに表示するアイテムの配列と、ユーザがアイテムを選択したときのアクションを受け取ることを定義するための DialogInterface.OnClickListener を与えます。
チェックボックスとラジオボタンの追加
複数選択アイテム (チェックボックス) や 排他選択アイテム (ラジオボタン) のリストをダイアログ内部に作成する場合は、それぞれ setMultiChoiceItems() 、setSingleChoiceItems() メソッドを使用します。 onCreateDialog() コールバックメソッドでいずれかの選択可能リストを作成した場合、 Android がリストの状態を管理してくれます。 アクティビティがアクティブである限りは前回選択されたアイテムを記憶しますが、ユーザがそのアクティビティを停止した時点でその選択は失われます。
注意: ユーザがアクティビティを放置したり一時停止したときに選択状態を保存する場合、 アクティビティのライフサイクル を考慮して設定を適切に保存し復元しなければなりません。アクティビティが完全に停止して場合でも永久に選択状態を保存するには データ保存 のテクニックを使用してその設定を保存する必要があります。.
右に表示されているような排他選択のリストがあるアラートダイアログを作成するには、前のサンプルと同じコードを使用しますが、以下のように setItems() メソッドを setSingleChoiceItems() に置き換えてください。
final CharSequence[] items = {"Red", "Green", "Blue"};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Pick a color");
builder.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();
}
});
AlertDialog alert = builder.create();
setSingleChoiceItems() メソッドの2番目のパラメータは、チェックされたアイテム を表す数値で、デフォルトの選択済みアイテムを0から始まるリストのインデックスで示しています。 "-1" の場合はデフォルトでなにも選択されていないことを示します。
プログレスダイアログの作成
ProgressDialog は AlertDialog クラスの拡張で、進行状況のアニメーションを タスクの進行状況が定義されていない場合はスピニングホイールの形式で、進行状況が定義されている場合はプログレスバーの形式で表示します。このダイアログはダウンロードをキャンセルするといったボタンも提供することができます。
プログレスダイアログをオープンするのは ProgressDialog.show() を呼び出すだけで簡単です。例えば、右のダイアログは onCreateDialog(int) コールバックを介してダイアログを管理することなく、以下のようにするすることで簡単に表示を実現できます。
ProgressDialog dialog = ProgressDialog.show(MyActivity.this, "",
"Loading. Please wait...", true);
最初のパラメータはアプリケーションの コンテキスト、2番目はダイアログのタイトル(空にしてあります)、3番目はメッセージ、そして最後のパラメータはこの進行状況が不確定かどうかの指定です(これはプログレスバーの作成に関連しますので次のセクションで説明します)。
プログレスダイアログのデフォルトのスタイルはスピニングホイールです。ロードの進捗を粒度で表示するプログレスバーを作成したい場合には次のセクションで説明しますが、もう少しコーディングが必要です。
プログレスバーの表示
アニメーション化されたプログレスバーの進捗状況は以下のように表示します。
- プログレスダイアログは、クラスコンストラクタのProgressDialog(Context) で初期化します。
- プログレススタイルは setProgressStyle(int) で "STYLE_HORIZONTAL" に設定し、メッセージなどのプロパティも設定します。
- ダイアログを表示する準備ができたら、 show() を呼び出すか、 onCreateDialog(int) コールバックからプログレスダイアログを返却するようにします。
- その時点での完了したパーセンテージの合計値を setProgress(int) に設定して呼び出すか、 その時点での完了したパーセンテージの増加分を incrementProgressBy(int) に設定して呼び出すかのいずれかでバーに表示された進捗の量を増加させることができます。
以下のサンプルはセットアップの一例です。
ProgressDialog progressDialog;
progressDialog = new ProgressDialog(mContext);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("Loading...");
progressDialog.setCancelable(false);
セットアップ自体は簡単です。プログレスダイアログを作成するためにやるべきことのほとんどは、それを更新するプロセスに実際に関連づけることです。 このことを実現するためには、アプリケーションに2番目のスレッドを作成し、 Handler オブジェクトでアクティビティのUI スレッドに進捗を報告することが必要であることが分かると思います。ハンドラで付加的なスレッドを使用することに慣れていない場合は、アクティビティで管理されているプログレスダイアログを増加させるために2番目のスレッドを使用している以下のアクティビティのサンプルを参照してください。
2番目のスレッドによるプログレスダイアログのサンプル
これはプロセスの進捗を追跡するために2番目のスレッドを使用しているサンプルです(実際は100までカウントアップしているだけです)。この2番目のスレッドは進捗が進むたびに、 Handler を介してメインのアクティビティに Message を送信しています。そのようにしてメインのアクティビティはプログレスダイアログを更新します。
package com.example.progressdialog;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class NotificationTest extends Activity {
static final int PROGRESS_DIALOG = 0;
Button button;
ProgressThread progressThread;
ProgressDialog progressDialog;
/** Called when the activity is first created. */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Setup the button that starts the progress dialog
button = (Button) findViewById(R.id.progressDialog);
button.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
showDialog(PROGRESS_DIALOG);
}
});
}
protected Dialog onCreateDialog(int id) {
switch(id) {
case PROGRESS_DIALOG:
progressDialog = new ProgressDialog(NotificationTest.this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("Loading...");
progressThread = new ProgressThread(handler);
progressThread.start();
return progressDialog;
default:
return null;
}
}
// Define the Handler that receives messages from the thread and update the progress
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
int total = msg.getData().getInt("total");
progressDialog.setProgress(total);
if (total >= 100){
dismissDialog(PROGRESS_DIALOG);
progressThread.setState(ProgressThread.STATE_DONE);
}
}
};
/** Nested class that performs progress calculations (counting) */
private class ProgressThread extends Thread {
Handler mHandler;
final static int STATE_DONE = 0;
final static int STATE_RUNNING = 1;
int mState;
int total;
ProgressThread(Handler h) {
mHandler = h;
}
public void run() {
mState = STATE_RUNNING;
total = 0;
while (mState == STATE_RUNNING) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Log.e("ERROR", "Thread Interrupted");
}
Message msg = mHandler.obtainMessage();
Bundle b = new Bundle();
b.putInt("total", total);
msg.setData(b);
mHandler.sendMessage(msg);
total++;
}
}
/* sets the current state for the thread,
* used to stop the thread */
public void setState(int state) {
mState = state;
}
}
}
カスタムダイアログの作成
ダイアログのデザインをカスタマイズしたい場合、ダイアログウィンドウに対して、レイアウトとウィジェット要素で独自のレイアウトを作成することができます。 レイアウトを定義したら、ルートの View オブジェクトまたはリソース ID を setContentView(View) に渡します。
以下は右に表示されているダイアログを作成するサンプルです。
- 以下のような XML レイアウトを作成し、
custom_dialog.xmlとして保存します。<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_root"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
>
<ImageView android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="10dp"
/>
<TextView android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:textColor="#FFF"
/>
</LinearLayout>この XML では ImageView と TextView を LinearLayout 内に定義しています。
- 上のレイアウトをダイアログのコンテントビューとして設定し、ImageView と TextView 要素の内容を以下のように定義します。
- たったこれだけです。 ダイアログの表示 で言っていたダイアログが表示できるようになりました。
Context mContext = getApplicationContext();
Dialog dialog = new Dialog(mContext);
dialog.setContentView(R.layout.custom_dialog);
dialog.setTitle("Custom Dialog");
TextView text = (TextView) dialog.findViewById(R.id.text);
text.setText("Hello, this is a custom dialog!");
ImageView image = (ImageView) dialog.findViewById(R.id.image);
image.setImageResource(R.drawable.android);
ダイアログをインスタンス化したら、レイアウトのリソース ID を setContentView(int) の引数として渡して呼び出すことにより、カスタムレイアウトをダイアログのコンテントビューとして設定します。これでダイアログは定義済みのレイアウトを保持したので、そのレイアウトから findViewById(int) で View オブジェクトを取得し、その内容を変更できるようになりました。
基底の Dialog クラスで作成したダイアログにはタイトルが必要です。 setTitle() を呼び出さないと、タイトルを空にするためにスペースが使われますが、見えています。タイトルが不要な場合は AlertDialog クラスを使用してカスタムダイアログを作成することを勧めます。 しかしながら、アラートダイアログは AlertDialog.Builder クラスで作成するのが最も簡単な方法なので、上記で使用した setContentView(int) メソッドへのアクセスは不要になります。その代わり setView(View) を使用する必要があります。このメソッドは View オブジェクトを受け付けるので、 XML からレイアウトのルートView オブジェクトをインフレートする必要があります。
XML レイアウトをインフレートするには、 getLayoutInflater() (または getSystemService()) で LayoutInflater を取得し、 inflate(int, ViewGroup)を呼び出します。最初のパラメータはレイアウトのリソース ID で2番目はルートView の ID です。この時点でレイアウトのView オブジェクトを見つけるためにインフレートされたレイアウトが使用可能となり、 ImageView と TextView 要素に対するコンテキストを定義できるようになります。それからAlertDialog.Builder をインスタンス化し、 setView(View) でダイアログにインフレートされたレイアウトを設定します。
以下はAlertDialog でカスタムレイアウトを作成するサンプルです。
AlertDialog.Builder builder;
AlertDialog alertDialog;
Context mContext = getApplicationContext();
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.custom_dialog,
(ViewGroup) findViewById(R.id.layout_root));
TextView text = (TextView) layout.findViewById(R.id.text);
text.setText("Hello, this is a custom dialog!");
ImageView image = (ImageView) layout.findViewById(R.id.image);
image.setImageResource(R.drawable.android);
builder = new AlertDialog.Builder(mContext);
builder.setView(layout);
alertDialog = builder.create();
カスタムレイアウトにアラートダイアログを使用することで、管理されたボタン、選択可能なリスト、タイトル、アイコン、その他のビルトインのアラートダイアログの機能が使用できるというメリットがあります。
より詳しい情報は、Dialog と AlertDialog.Builder クラスのリファレンスドキュメントを参照してください。