ダイアログ (5) 日付と時間の選択ダイアログ その2 ================================================================================ ■時刻の選択ダイアログ 時刻を選択するダイアログは、TimePickerDialog を使う。 とくに何もしなくても、タイトルや選択するボタンなどは、日本語で表示してくれる。 また、DatePickerDialog と違って、時刻のタイトルは、mm:dd の形式なので、とくに変 更する必要はない。 □ Dialog08Activity.java --- package jp.marunomaruno.android.sample; import java.util.Calendar; import android.app.Activity; import android.app.TimePickerDialog; import android.os.Bundle; import android.widget.TextView; import android.widget.TimePicker; public class Dialog08Activity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); TextView date = (TextView) findViewById(R.id.date); Calendar calendar = Calendar.getInstance(); int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY); int minute = calendar.get(Calendar.MINUTE); TimePickerDialog dialog = new TimePickerDialog(this, new DateSetHandler(date), hourOfDay, minute, true); // (1) dialog.show(); } private class DateSetHandler implements TimePickerDialog.OnTimeSetListener { // (2) private TextView date; public DateSetHandler(TextView date) { this.date = date; } @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { // (3) Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, hourOfDay); calendar.set(Calendar.MINUTE, minute); date.setText(String.format(getString(R.string.time_format), calendar)); } } } --- □ TimePickerDialog dialog = new TimePickerDialog(this, new DateSetHandler(date), hourOfDay, minute, true); // (1) 時刻を設定する TimePickerDialog オブジェクトを生成する。 このとき、時刻が設定されたときのリスナー、最初に表示される時・分を指定する。 ・クラス階層 java.lang.Object + android.app.Dialog + android.app.AlertDialog + android.app.TimePickerDialog ・主なコンストラクター --- TimePickerDialog(Context context, TimePickerDialog.OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView) TimePickerDialog(Context context, int theme, TimePickerDialog.OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView) --- 時刻が設定されたときのリスナーのオブジェクトや、最初に表示される時刻、24時間表示 にするかどうかを引数として渡す。 □ private class DateSetHandler implements TimePickerDialog.OnTimeSetListener { // (2) 時刻が設定されたときのリスナー TimePickerDialog.OnTimeSetListener の実装クラスを 定義する。 □ TimePickerDialog.OnTimeSetListener 時刻が設定されたときのリスナーのインターフェース。 実装すべきメソッドは以下のメソッド。 abstract void onTimeSet(TimePicker view, int hourOfDay, int minute) □ public void onTimeSet(TimePicker view, int hourOfDay, int minute) { // (3) 時刻が設定されたときのハンドラー・メソッド。 変更された時刻は、引数の hourOfDay, minute に入っている。 ※ --- onDateSet() もそうだが、view から、設定されている時刻を取得できるので、引数の hourOfDay, minute はいらないと思うが、どうなんだろう。 単に使いやすさのためかな。 --- □ values/strings.xml --- <resources> <string name="hello">Hello World, Dialog08Activity!</string> <string name="app_name">Dialog08</string> <string name="time_format">%tR</string> </resources> --- ■日付と時刻の選択ダイアログ このサンプルは、日時を指定するのではなく、日付と日時をそれぞれ、別のダイアログで 指定するもの。べつべつに指定したものをリスナーでまとめている。 □ Dialog09Activity.java --- package jp.marunomaruno.android.sample; import java.util.Calendar; import android.app.Activity; import android.app.DatePickerDialog; import android.app.TimePickerDialog; import android.os.Bundle; import android.widget.DatePicker; import android.widget.TextView; import android.widget.TimePicker; public class Dialog09Activity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); TextView date = (TextView) findViewById(R.id.date); Calendar calendar = Calendar.getInstance(); int year = calendar.get(Calendar.YEAR); int monthOfYear = calendar.get(Calendar.MONTH); int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY); int minute = calendar.get(Calendar.MINUTE); TimePickerDialog timePickerDialog = new TimePickerDialog(this, new DateSetHandler(date), hourOfDay, minute, true); // (1) timePickerDialog.show(); DatePickerDialog datePickerDialog = new DatePickerDialog(this, new DateSetHandler(date), year, monthOfYear, dayOfMonth); // (2) datePickerDialog.show(); } private class DateSetHandler implements DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener { // (3) private TextView date; private Calendar calendar = Calendar.getInstance(); // (4) public DateSetHandler(TextView date) { this.date = date; } @Override public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { // (5) calendar.set(year, monthOfYear, dayOfMonth); // (6) date.setText(String.format(getString(R.string.date_time_format), calendar)); // (7) } @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { // (8) calendar.set(Calendar.HOUR_OF_DAY, hourOfDay); // (9) calendar.set(Calendar.MINUTE, minute); // (10) date.setText(String.format(getString(R.string.date_time_format), calendar)); // (11) } } } --- □ TimePickerDialog timePickerDialog = new TimePickerDialog(this, new DateSetHandler(date), hourOfDay, minute, true); // (1) □ DatePickerDialog datePickerDialog = new DatePickerDialog(this, new DateSetHandler(date), year, monthOfYear, dayOfMonth); // (2) 日付、時刻の順で設定するので、ダイアログは、TimePickerDialog、DatePickerDialog の順番に表示する。 表示するのが時刻、日付の順番でよいので、実は生成の順番はこのとおりでなくてもよい。 □ private class DateSetHandler implements DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener { // (3) 日時の変更に対処するために、DatePickerDialog.OnDateSetListener と、TimePickerDia log.OnTimeSetListener の 2 つのインターフェースを実装するリスナーのクラスをつく る。 □ private Calendar calendar = Calendar.getInstance(); // (4) onDateSet() メソッドと、onTimeSet() メソッドの2つで使うので、インスタンス変数と して定義する。 □ public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { // (5) □ calendar.set(year, monthOfYear, dayOfMonth); // (6) 日付が設定されたときのメソッド。ここで、calendar に日付部分を設定する。 □ date.setText(String.format(getString(R.string.date_time_format), calendar)); // (7) □ date.setText(String.format(getString(R.string.date_time_format), calendar)); // (11) 設定された日時をテキストビューに設定する。フォーマットは以下のとおり。 <string name="date_time_format">%1$tF %1$tR</string> すなわち、 yyyy-mm-dd hh:mm ※補足 --- 今回のサンプルでは使用していないが、2 つ以上の書式指示子を指定する場合、引数の位 置(argument_index$)を指定する必要がある。そうしないと、つぎの strings.xml では、 次のエラーになり、プロジェクトのビルドができない。 Multiple annotations found at this line: - error: Multiple substitutions specified in non-positional format; did you mean to add the formatted="false" attribute? - error: Unexpected end tag string また、直接、Java ソースで指定すると、実行時のエラーとなる。 このことからも、文字列リテラルは直接ソース上でハードコーディングせずに、 strings.xml などのリソースとして定義しておいたほうがよい。 なお、この仕様は Android の仕様で、Java の仕様ではない。Java の場合は、引数の位 置を指定しないときは、引数が順番に割り振られる。 --- □ public void onTimeSet(TimePicker view, int hourOfDay, int minute) { // (8) □ calendar.set(Calendar.HOUR_OF_DAY, hourOfDay); // (9) □ calendar.set(Calendar.MINUTE, minute); // (10) 時刻が設定されたときのメソッド。ここで、calendar に時刻部分を設定する。 □ values/strings.xml --- <resources> <string name="hello">Hello World, Dialog09Activity!</string> <string name="app_name">Dialog09</string> <string name="date_time_format">%1$tF %1$tR</string> </resources> --- ■ 日時を同時に指定するダイアログ 日付と日時をべつべつのダイアログで設定するのではなく、ひとつのダイアログにまとめ て設定する。これも、カスタムダイアログになるので、ダイアログのクラスは AlertDialog を使う。 カスタムビューとしては、DatePicker と TimePicker を LinearLayout で組み合わせた ものを使う。 □ Dialog091Activity.java --- package jp.marunomaruno.android.sample; import java.util.Calendar; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Bundle; import android.util.Log; import android.view.ViewGroup; import android.widget.DatePicker; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.TimePicker; public class Dialog091Activity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 結果を設定するビューを取得する TextView date = (TextView) findViewById(R.id.date); // ダイアログに表示する日時を取得する Calendar calendar = Calendar.getInstance(); int year = calendar.get(Calendar.YEAR); int monthOfYear = calendar.get(Calendar.MONTH); int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY); int minute = calendar.get(Calendar.MINUTE); // 2つのpickerを併せたビューを作る DatePicker datePicker = new DatePicker(this); // (1) TimePicker timePicker = new TimePicker(this); // (2) LinearLayout view = new LinearLayout(this); // (3) view.setOrientation(LinearLayout.VERTICAL); // (4) view.addView(datePicker); // (5) view.addView(timePicker); // (6) // 日時を設定するビューを使ったダイアログを生成する AlertDialog dialog = new AlertDialog.Builder(this) // (7) .setView(view) // (8) .setTitle( String.format(getString(R.string.date_time_format), calendar, calendar)) .setPositiveButton(R.string.set_title, new DateSetHandler(view, date)) // (9) .setNegativeButton(R.string.cancel_title, null).show(); // pickerに値が変化したことを検知するハンドラーを設定する DateChangedHandler handler = new DateChangedHandler(dialog); // (10) datePicker.init(year, monthOfYear, dayOfMonth, handler); // (11) timePicker.setCurrentHour(hourOfDay); // (12) timePicker.setCurrentMinute(minute); // (13) timePicker.setOnTimeChangedListener(handler); // (14) timePicker.setIs24HourView(true); // (15) } /** * 日時が変更されたときの処理をするハンドラー */ private class DateChangedHandler implements DatePicker.OnDateChangedListener, TimePicker.OnTimeChangedListener { private AlertDialog dialog; private Calendar calendar = Calendar.getInstance(); public DateChangedHandler(AlertDialog dialog) { this.dialog = dialog; } @Override public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) { calendar.set(year, monthOfYear, dayOfMonth); Log.d("TEST", String.format("d: %tc", calendar)); dialog.setTitle(String.format(getString(R.string.date_time_format), calendar)); } @Override public void onTimeChanged(TimePicker view, int hourOfDay, int minute) { calendar.set(Calendar.HOUR_OF_DAY, hourOfDay); calendar.set(Calendar.MINUTE, minute); Log.d("TEST", String.format("t: %tc", calendar)); dialog.setTitle(String.format(getString(R.string.date_time_format), calendar)); } } /** * 日時が設定されたときの処理をするハンドラー */ private class DateSetHandler implements DialogInterface.OnClickListener { // (16) private ViewGroup view; private TextView date; public DateSetHandler(ViewGroup view, TextView date) { this.view = view; this.date = date; } @Override public void onClick(DialogInterface dialog, int which) { // (17) DatePicker datePicker = (DatePicker) view.getChildAt(0); // (18) TimePicker timePicker = (TimePicker) view.getChildAt(1); // (19) Calendar calendar = Calendar.getInstance(); calendar.set(datePicker.getYear(), datePicker.getMonth(), datePicker.getDayOfMonth(), timePicker.getCurrentHour(), timePicker.getCurrentMinute()); Log.d("TEST", String.format("s: %tc", calendar)); date.setText(String.format(getString(R.string.date_time_format), calendar)); } } } --- □ DatePicker datePicker = new DatePicker(this); // (1) □ TimePicker timePicker = new TimePicker(this); // (2) カスタムビューとして作るために、DatePicker、TimePicker のオブジェクトを生成する。 2 つのビューなので、これを、つぎの LinearLayout にまとめて、たてに 2 つのピッ カーが表示されるようにする。 DatePicker については既出。 TimePicker は、日時を設定できるビューのクラス。 ・クラス階層 java.lang.Object + android.view.View + android.view.ViewGroup + android.widget.FrameLayout + android.widget.TimePicker ・コンストラクター --- TimePicker(Context context) TimePicker(Context context, AttributeSet attrs) TimePicker(Context context, AttributeSet attrs, int defStyle) --- □ LinearLayout view = new LinearLayout(this); // (3) □ view.setOrientation(LinearLayout.VERTICAL); // (4) □ view.addView(datePicker); // (5) □ view.addView(timePicker); // (6) DatePicker、TimePicker のオブジェクトを 2 つ縦に並べるレイアウトとして、LinearLa yout オブジェクトを生成し、たてに設定して、DatePicker、TimePicker のオブジェクト を追加する。 なお、LinearLayout は、ViewGroup のサブクラスになっているので、つぎのメソッドで、 ビューを追加できる。 ViewGroup の addView() メソッド --- void addView(View child, int index, ViewGroup.LayoutParams params) void addView(View child, ViewGroup.LayoutParams params) void addView(View child, int index) void addView(View child) void addView(View child, int width, int height) --- 引数 index を指定しないものでは、(おそらく) 0 から順番にindex が振られて追加され る。 □ AlertDialog dialog = new AlertDialog.Builder(this) // (7) □ .setView(view) // (8) カスタムダイアログとして、AlertDialog オブジェクトを生成し、LinearLayout のオブ ジェクトをカスタムビューとして設定する。 □ .setPositiveButton(R.string.set_title, new DateSetHandler(view, date)) // (9) 設定ボタンを設定する。リスナーのオブジェクトは、DialogInterface.OnClickListener を実装する。 □ DateChangedHandler handler = new DateChangedHandler(dialog); // (10) 日時を変えることに対するリスナーのオブジェクトを生成する。 このオブジェクトは、DatePicker と TimePicker オブジェクトの初期化のところで使用 する。 □ datePicker.init(year, monthOfYear, dayOfMonth, handler); // (11) □ timePicker.setCurrentHour(hourOfDay); // (12) □ timePicker.setCurrentMinute(minute); // (13) □ timePicker.setOnTimeChangedListener(handler); // (14) □ timePicker.setIs24HourView(true); // (15) 現在日時と、(10) で作った日時を変えることに対するリスナーのオブジェクトを使って、 DatePicker と TimePicker オブジェクトを初期化する。 初期化には、DatePicker クラスでは、次のメソッドを使う。 void init(int year, int monthOfYear, int dayOfMonth, DatePicker.OnDateChangedListener onDateChangedListener) また、TimePicker クラスでは、次のメソッド。なぜか、init メソッドがない。ちょっと 不思議な感じ。 void setCurrentHour(Integer currentHour) void setCurrentMinute(Integer currentMinute) void setOnTimeChangedListener(TimePicker.OnTimeChangedListener onTimeChangedListener) void setIs24HourView(Boolean is24HourView) TimePicker のデフォルトの表示形式は、12 時間表示になっているので、 setIs24HourView(true) を指定しないと、24 時間表示にはならない。 □ private class DateSetHandler implements DialogInterface.OnClickListener { // (16) □ public void onClick(DialogInterface dialog, int which) { // (17) 設定ボタンに対するリスナーの実装クラスと、そのハンドラーメソッド。 □ DatePicker datePicker = (DatePicker) view.getChildAt(0); // (18) □ TimePicker timePicker = (TimePicker) view.getChildAt(1); // (19) このクラスには、日時の設定ダイアログで使ったビュー(ViewGroup の型で、実際のオブ ジェクトは LinearLayout)を渡している。 そのビューのオブジェクトから、そこに設定しているそれぞれのピッカーのオブジェクト を取得する。 ・ViewGroup で、子ビューを取得するメソッド --- View getChildAt(int index) --- index は、addView() メソッドで指定した順番。 □ values/strings.xml --- <resources> <string name="hello">Hello World, Dialog091Activity!</string> <string name="app_name">Dialog091</string> <string name="date_format">%tF</string> <string name="time_format">%tR</string> <string name="date_time_format">%1$tF %1$tR</string> </resources> --- 以上