センサー (1) 光センサーと加速度センサー ================================================================================ 各種センサーを使うことができる。 ■ センサーのタイプ Sensor クラスの定数で定義してある。以下にまとめる。 センサー名 定数 単位 値の次元 可 ------------------- --------------------------- ------- -------- ------ 加速度 TYPE_ACCELEROMETER m/s^2 3 ○ 重力 TYPE_GRAVITY m/s^2 3 ※ ジャイロスコープ TYPE_GYROSCOPE rad/s 3 × 光 TYPE_LIGHT lux 1 ○ 線形加速 TYPE_LINEAR_ACCELERATION m/s^2 3 ※ 地磁気 TYPE_MAGNETIC_FIELD 3 ○ 圧力 TYPE_PRESSURE Pa 1 × 近接 TYPE_PROXIMITY cm 1 ○ 回転ベクトル TYPE_ROTATION_VECTOR - 4 ※ 温度 TYPE_TEMPERATURE ℃ 1 ○ 傾き TYPE_ORIENTATION deg 3 非推奨 (○あり、×なし、※Android2.2では対象外) ■ センサーのタイプと値の取得方法 センサーの値を取得するには、つぎの手順が必要。 1. SensorManager のオブジェクトを取得する 2. SensorManager オブジェクトにセンサーのリスナーを登録する 3. センサーの値を取得する ここでは、値の取得について補足する。 どのセンサーでも、センサーのリスナーの void onSensorChanged(SensorEvent event) メソッドの event に、そのときの値が入っている。これのインスタンス変数 values が 配列になっており、センサーからの取得値の次元によって、要素数が決まる。 以下にそれぞれのセンサーについての values について補足する。 □ TYPE_ACCELEROMETER 加速度センサー values[0]: X 軸方向の加速度 values[1]: Y 軸方向の加速度 values[2]: Z 軸方向の加速度 X 軸: 実機の横方向(右方向が正) Y 軸: 実機のたて方向(上方向が正) Z 軸: 実機の前面方向(前面方向が正) SensorEvent クラスの API に図入りで説明がある。 □ TYPE_GRAVITY 重力センサー ※Android2.2 にはない □ TYPE_GYROSCOPE ジャイロスコープ・センサー values[0]: X 軸方向の角速度 values[1]: Y 軸方向の角速度 values[2]: Z 軸方向の角速度 □ TYPE_LIGHT 光センサー values[0]: 照度 □ TYPE_LINEAR_ACCELERATION 線形加速センサー ※Android2.2 にはない □ TYPE_MAGNETIC_FIELD 地磁気センサー values[0]: X 軸方向の磁気密度 values[1]: Y 軸方向の磁気密度 values[2]: Z 軸方向の磁気密度 □ TYPE_PRESSURE 圧力センサー □ TYPE_PROXIMITY 近接センサー values[0]: 距離 □ TYPE_ROTATION_VECTOR 回転ベクトル・センサー ※Android2.2 にはない □ TYPE_TEMPERATURE 温度センサー APIには特に記述はないが、摂氏で取得される。 □ TYPE_ORIENTATION 傾きセンサー ただし、この定数は非推奨になったので、このセンサーの値は、この定数を使うのではな く、SensorManager.getOrientation() メソッドを使う。 ■ 光センサーの値を表示する 光センサーの値を取得して、表示する。 単位: lux (ルクス) values[0]: 照度 センサーの値を取得するには、つぎの手順が必要。 1. SensorManager のオブジェクトを取得する 2. SensorManager オブジェクトにセンサーのリスナーを登録する 3. センサーの値を取得する これにしたがって、プログラムを作る。 □ LightSensor01Activity.java --- package jp.marunomaruno.android.sample; import android.app.Activity; import android.hardware.Sensor; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.widget.TextView; /** * 光センサーの値を表示する。 * @author marunomaruno * @version 1.0, 2011-06-25 */ public class LightSensor01Activity extends Activity { // TextView private TextView lightTextView; // 光センサー private SensorManager sensorManager; // (1) private SensorEventListener sensorEventListener; // onPause()が呼ばれたときに、すべてのリスナーを解除するため // (2) @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); lightTextView = (TextView) findViewById(R.id.sensor_value); sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);// (3) } @Override protected void onResume() { super.onResume(); // 光センサーを取得して登録する sensorEventListener = new LightSensorListener(lightTextView); // (4) for (Sensor sensor : sensorManager.getSensorList(Sensor.TYPE_LIGHT)) {// (5) sensorManager.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL); // (6) } } @Override public void onPause() { // (7) super.onPause(); // すべてのリスナーを解除する sensorManager.unregisterListener(sensorEventListener); // (8) } } --- (1)(3) SensorManager を宣言する。 private SensorManager sensorManager; // (1) sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);// (3) SensorManager は、センサーにアクセスするためのクラス。 ・クラス階層 java.lang.Object + android.hardware.SensorManager public なコンストラクターはない。オブジェクトの取得は、(3) のように、 getSystemService(SENSOR_SERVICE) を使う。 実際は、 Context.getSystemService(Context.SENSOR_SERVICE) だが、Activity クラスのサブクラスの中から取得しているため、「Context.」部分は必 要ない。 ・主なメソッド --- List<Sensor> getSensorList(int type) boolean registerListener(SensorEventListener listener, Sensor sensor, int rate, Handler handler) boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) void unregisterListener(SensorEventListener listener, Sensor sensor) void unregisterListener(SensorEventListener listener) --- メソッドとしては、次のような使い方の構成になっている。 ・Android 実機から、使えるセンサーのリストを getSensorList() メソッドで取得する。 ・それを、registerListener() メソッドを使って、センサーと、そのリスナーとを紐付 ける。 ・センサーが必要なくなったら、unregisterListener() で、紐付けを解除する。 (2)(8) onPause()が呼ばれたときに、すべてのリスナーを解除するために、センサーのリ スナーを登録しておくリストを宣言する。 private SensorEventListener sensorEventListener; // onPause()が呼ばれたときに、すべてのリスナーを解除するため // (2) リスナーの解除は、(8) にあるように、SensorManager クラスの unregisterListener(listener) メソッドを使う。 sensorManager.unregisterListener(sensorEventListener); // (8) (4) 光センサーのリスナーを宣言する sensorEventListener = new LightSensorListener(lightTextView); // (4) 光センサー・リスナーは、別クラスとして作っている。 別クラスとして作ることで、単体テストなどはしやすくなる。また、他のアプリケーショ ンでの再利用もしやすくなる利点がある。 (5) for (Sensor sensor : sensorManager.getSensorList(Sensor.TYPE_LIGHT)) {// (5) 光センサーのディバイスを取得する。1台の実機に、複数の光センサーがついている可能 性があるので、戻り値はリストになっている。 List<Sensor> getSensorList(int type) 引数の type は、センサーの種類を表す定数で、Sensor クラスに、つぎのような定数が 定義されている。 センサー名 定数 単位 値の次元 可 ------------------- --------------------------- ------- -------- ------ 加速度 TYPE_ACCELEROMETER m/s^2 3 ○ 重力 TYPE_GRAVITY m/s^2 3 ※ ジャイロスコープ TYPE_GYROSCOPE rad/s 3 × 光 TYPE_LIGHT lux 1 ○ 線形加速 TYPE_LINEAR_ACCELERATION m/s^2 3 ※ 地磁気 TYPE_MAGNETIC_FIELD 3 ○ 圧力 TYPE_PRESSURE Pa 1 × 近接 TYPE_PROXIMITY cm 1 ○ 回転ベクトル TYPE_ROTATION_VECTOR - 4 ※ 温度 TYPE_TEMPERATURE ℃ 1 ○ 傾き TYPE_ORIENTATION deg 3 非推奨 (○あり、×なし、※Android2.2では対象外) また、TYPE_ALL という定数があり、これですべてのセンサーのオブジェクトを取得でき る。 Sensor クラス ・クラス階層 java.lang.Object + android.hardware.Sensor ・主なメソッド --- float getMaximumRange() int getMinDelay() String getName() float getPower() float getResolution() int getType() String getVendor() int getVersion() --- (6) センサーのリスナーをセンサーに登録する sensorManager.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL); // (6) つぎのメソッドがある。 --- boolean registerListener(SensorEventListener listener, Sensor sensor, int rate, Handler handler) boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) --- 今回は、光センサーに、光センサーのリスナーを登録する。登録は、紐付けしていること になる。 rate は、SensorManager クラスでつぎの 4 つの定数が定義されている。 SENSOR_DELAY_NORMAL, SENSOR_DELAY_UI, SENSOR_DELAY_GAME, SENSOR_DELAY_FASTEST これらは、定数の値としては 3, 2, 1, 0 なので、直接遅延時間になっているわけではな い。 実際の遅延時間の値を確認するには、Sensor クラスに int getMinDelay() があるが、これは API レベル 9 なので、Android2.2 では使えない。 SensorManager クラスのソースで見てみると、次のような値になっている(実際はμ秒 値)。 SENSOR_DELAY_FASTEST 0 ms ( 0 μs) SENSOR_DELAY_GAME 20 ms ( 20000 μs) SENSOR_DELAY_UI 60 ms ( 60000 μs) SENSOR_DELAY_NORMAL 200 ms (200000 μs) そして、0~3 以外の値であれば、その値が直接遅延時間として採用される。 (7)(8) アクティビティがバックグランドに隠れる直前に、センサーのリスナーを解除す る public void onPause() { // (7) onPause() メソッドは、アクティビティがバックグランドに隠れる直前(他のアクティビ ティが起動、たとえば、着信があった、電話をかける)に呼ばれる。 センサーが動いていれば、その分、電池が消耗するので、隠れるときにはセンサーをいっ たん停止させる。 sensorManager.unregisterListener(sensorEventListener); // (8) 解除には、 void unregisterListener(SensorEventListener listener, Sensor sensor) void unregisterListener(SensorEventListener listener) を使う。sensor の指定がなければ、リスナーが登録されているすべてのセンサーから解 除する。 □ LightSensorListener.java --- package jp.marunomaruno.android.sample; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.widget.TextView; /** * 光センサーのリスナー * @author maruno * @version 1.0, 2011-06-23 * @since 1.0 */ public class LightSensorListener implements SensorEventListener { // (1) protected TextView textView; public LightSensorListener(TextView textView) { this.textView = textView; } @Override public void onSensorChanged(SensorEvent event) { // (2) // 値を取得する float x = event.values[0]; // (3) // TextViewに表示する textView.setText(String.format( textView.getResources().getString(R.string.sensor_value_format), textView.getResources().getString(R.string.light), event.sensor.getName(), x, textView.getResources().getString(R.string.lightUnit) )); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // (4) assert true; // 何もしない } } --- (1) 光センサーのリスナーの定義 public class LightSensorListener implements SensorEventListener { // (1) センサーに変化があったときに使うリスナー・クラスを定義する。 SensorEventListener センサーの値が変わったときに呼び出されるリスナーのインターフェース ・メソッド --- abstract void onAccuracyChanged(Sensor sensor, int accuracy) センサーの精度が変わったとき abstract void onSensorChanged(SensorEvent event) センサーからの値が変わったとき --- (2)(3) センサーの値が変化したときに呼ばれるハンドラー・メソッドとそのときの値の取 得 public void onSensorChanged(SensorEvent event) { // (2) float x = event.values[0]; // (3) 引数の event から、値を取得する。 光センサーは、1次元の値を取得するので、values は、要素数 1 である。 SensorEvent センサーから生成されたイベントを表すクラス。 ・クラス階層 java.lang.Object + android.hardware.SensorEvent ・public フィールド --- public int accuracy 精度 public Sensor sensor このイベントを生成したセンサー public long timestamp イベントが起きたタイムスタンプ public final float[] values 値。配列の長さは、値の次元数 --- (4) センサーの精度が変更されると呼ばれるハンドラー・メソッド public void onAccuracyChanged(Sensor sensor, int accuracy) { // (4) 通常は、このメソッドが使われることはない(らしい)。 □ res/layout/main.xml --- <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <!-- センサー値 --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/light" android:id="@+id/sensor_value" /> </LinearLayout> --- □ res/values/strings.xml --- <?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">LightSensor01</string> <string name="light">光</string> <string name="lightUnit">lux</string> <string name="sensor_value_format">%1$s: %2$s = %3$.2f (%4$s)</string> </resources> --- センサーの値は、次の形式で表示する。 センサー名: オブジェクトの情報 = 値 (単位) ■ 加速度センサーの値を表示する 加速度センサーの値を取得して、表示する。 加速度センサーは、3 次元の値として取得する。単位は、m/s^2。 値は、SensorEvent の次のフィールドを使って取得する。 values[0]: X 軸方向の加速度 values[1]: Y 軸方向の加速度 values[2]: Z 軸方向の加速度 X 軸: 実機の横方向(右方向が正) Y 軸: 実機のたて方向(上方向が正) Z 軸: 実機の前面方向(前面方向が正) なお、SensorEvent クラスの API に図入りで説明がある。 □ AcceleraterSensor01Activity.java --- package jp.marunomaruno.android.sample; import android.app.Activity; import android.hardware.Sensor; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.widget.TextView; /** * 加速度センサーの値を表示する。 * * @author marunomaruno * @version 1.0, 2011-06-25 */ public class AcceleraterSensor01Activity extends Activity { // TextView private TextView acceleraterTextView; // 加速度センサー private SensorManager sensorManager; private SensorEventListener sensorEventListener; // onPause()が呼ばれたときに、すべてのリスナーを解除するため @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); acceleraterTextView = (TextView) findViewById(R.id.sensor_value); sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); } @Override protected void onResume() { super.onResume(); // 加速度センサーを取得して登録する sensorEventListener = new AcceleraterSensorListener( acceleraterTextView); // (1) for (Sensor sensor : sensorManager.getSensorList( Sensor.TYPE_ACCELEROMETER)) { // (2) sensorManager.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL); } } @Override public void onPause() { super.onPause(); // すべてのリスナーを解除する sensorManager.unregisterListener(sensorEventListener); } } --- (1)(2) 加速度センサーを取得して登録 sensorEventListener = new AcceleraterSensorListener(acceleraterTextView); // (1) 加速度センサーのオブジェクトを生成する。 for (Sensor sensor : sensorManager.getSensorList( Sensor.TYPE_ACCELEROMETER)) { // (2) 加速度センサーのデバイスのオブジェクトを取得する。これには、定数 TYPE_ACCELEROMETER を使う。 □ AcceleraterSensorListener.java --- package jp.marunomaruno.android.sample; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.widget.TextView; /** * 加速度センサーのリスナー * @author maruno * @version 1.0, 2011-06-23 * @since 1.0 */ public class AcceleraterSensorListener implements SensorEventListener { protected TextView textView; public AcceleraterSensorListener(TextView textView) { this.textView = textView; } @Override public void onSensorChanged(SensorEvent event) { // 値を取得する float x = event.values[0]; // (1) float y = event.values[1]; // (2) float z = event.values[2]; // (3) // TextViewに表示する textView.setText(String.format( textView.getResources().getString(R.string.sensor_value_format), textView.getResources().getString(R.string.accelerater), event.sensor.getName(), x, y, z, textView.getResources().getString(R.string.acceleraterUnit) )); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { assert true; // 何もしない } } --- (1)(2)(3) センサーの値を取得 float x = event.values[0]; // (1) float y = event.values[1]; // (2) float z = event.values[2]; // (3) 加速度センサーは 3 次元の値を持つので、values は要素数 3 の配列になる。それぞれ、 x, y, z として取得する。 □ res/layout/main.xml --- <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <!-- センサー値 --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/accelerater" android:id="@+id/sensor_value" /> </LinearLayout> --- □ res/values/strings.xml --- <?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">AcceleraterSensor01</string> <string name="accelerater">加速度</string> <string name="acceleraterUnit">m/s^2</string> <string name="sensor_value_format">%1$s: %2$s = (X, Y, Z) = (%3$.2f, %4$.2f, %5$.2f) (%6$s)</string> </resources> ---
基本クラスのコンストラクターでスローされた例外を派生クラスのコンストラクターの初
期化リストでもキャッチする
================================================================================
g++ではできるが、bcc32、VC++6では、コンパイルエラーになる。
つぎのように、派生クラスのコンストラクター部分を、初期化リストも含めて try-catch
で囲む。
この場合、初期化リストでも、コンストラクターの中身でも例外がキャッチされる。
□ ConstractorExceptionSample.cpp
---
#include <iostream>
#include <exception>
using namespace std;
class A {
public:
A() {
cout << "*A" << endl;
throw exception(); // 基本クラスで例外をスロー
}
};
class B : public A {
public:
B() try // 派生クラスのコンストラクターをtry-catchで囲む
: A() // 初期化リスト
{
cout << "*B" << endl; // コンストラクターの中身
} catch(exception &e) {
throw e;
}
};
int main()
{
try {
cout << "*1" << endl;
B b;
cout << "*2" << endl;
} catch(exception &e) {
cerr << e.what() << endl;
}
cout << "*3" << endl;
}
---
□ 実行結果(exception をスローした場合)
---
*1
*A
*3
St9exception <-- cerr なので、出る場所はこことは限らない
---
□ 実行結果(exception をスローしなかった場合)
---
*1
*A
*B
*2
*3
---
</pre>
ダイアログ (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> --- 以上
ダイアログ (5) 日付の選択ダイアログ ================================================================================ 日付と時刻を選択するダイアログ。 それぞれ、DatePickerDialog と TimePickerDialog を使う。 また、日時を同時に指定するには、自分で、日付と時刻を選択するダイアログを作る必要 がある。これは、カスタムダイアログとして、AlertDialog を使う。このとき、カスタム ビューとしては、日付と時刻を選択するための DatePicker と TimePicker を組み合わせ て使う。 それぞれのサンプルは以下のとおり。 Dialog07 日付の選択ダイアログ Dialog071 日付の選択ダイアログで、タイトルの形式をカスタマイズ Dialog08 時刻の選択ダイアログ ※ Dialog09 日付と時刻の選択ダイアログが連続して設定 ※ Dialog091 日時の選択ダイアログ(カスタムダイアログ) ※ ※(6)で紹介 ■ 日付の選択ダイアログ 日付を選択するダイアログは、DatePickerDialog を使う。 とくに何もしなくても、タイトルや設定ボタン、キャンセル・ボタンなどは、日本語で表 示してくれる。 ただ、タイトルの形式は、次の形式で表示される。 土曜日, 8月 20, 2011 この形式の変更は、次のサンプル Dialog071 で補足する。 □ Dialog07Activity.java --- package jp.marunomaruno.android.sample; import java.util.Calendar; import android.app.Activity; import android.app.DatePickerDialog; import android.os.Bundle; import android.widget.DatePicker; import android.widget.TextView; public class Dialog07Activity 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(); // (1) int year = calendar.get(Calendar.YEAR); int monthOfYear = calendar.get(Calendar.MONTH); int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); DatePickerDialog dialog = new DatePickerDialog(this, new DateSetHandler(date), year, monthOfYear, dayOfMonth); // (2) // dialog.setTitle(String.format(getString(R.string.date_format), calendar)); // (3) dialog.show(); } private class DateSetHandler implements DatePickerDialog.OnDateSetListener { // (4) private TextView date; public DateSetHandler(TextView date) { this.date = date; } @Override public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { // (5) Calendar calendar = Calendar.getInstance(); calendar.set(year, monthOfYear, dayOfMonth); date.setText(String.format(getString(R.string.date_format), calenda r)); // (6) } } } --- □ Calendar calendar = Calendar.getInstance(); // (1) ダイアログには、デフォルトで今日の日付を表示するので、Calendar.getInstance() で、 カレンダー日付のオブジェクトを設定する。 □ DatePickerDialog dialog = new DatePickerDialog(this, new DateSetHandler(date), year, monthOfYear, dayOfMonth); // (2) DatePickerDialog オブジェクトを生成する。 このとき、日付が設定されたときのリスナー、最初に表示される年・月・日を指定する。 ・クラス階層 java.lang.Object + android.app.Dialog + android.app.AlertDialog + android.app.DatePickerDialog ・コンストラクター --- DatePickerDialog(Context context, DatePickerDialog.OnDateSetListener callBack, int year, int monthOfYear, int dayOfMonth) DatePickerDialog(Context context, int theme, DatePickerDialog.OnDateSetListener callBack, int year, int monthOfYear, int dayOfMonth) --- □ // dialog.setTitle(String.format(getString(R.string.date_format), calendar)); // (3) コメントアウトされているが、setTitle() を使うことで、タイトルを設定する。ただし、 日付が変わったら、ここで指定した文字列は壊れ、デフォルトのタイトルの形式になる。 デフォルトのタイトルは、次の形式で表示される。 土曜日, 8月 20, 2011 これの形式を変更するのは、次に示すサンプル Dialog071 で示す。 □ private class DateSetHandler implements DatePickerDialog.OnDateSetListener { // (4) □ public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { // (5) ダイアログで日付を設定したときに呼び出されるリスナー DatePickerDialog.OnDateSetL istener を実装するクラスの定義。 DatePickerDialog.OnDateSetListener 日付を設定し終えて、設定ボタンをクリックしたときに呼ばれるリスナー・インターフ ェース。 つぎのメソッドを実装する。 abstract void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) 最終的に確定した日付が引数で受け取れる。ただし、monthOfYear は、Calendar クラス と同じで、0~11 の値でくる。 今回は、受け取ったテキストビューに、この日付を組み立てて設定している。 ※ --- このメソッドでは、引数 view から、設定されている日付を取得できるので、引数の year, monthOfYear, dayOfMonth はいらないと思うが、どうなんだろう。 単に使いやすさのためかな。 --- □ date.setText(String.format(getString(R.string.date_format), calendar));// (6) 最終的な日付をテキストビューに設定している。 形式は、リソース values/strings.xml で定義している。 内容は、以下のとおり。 <string name="date_format">%tF</string> これは、ISO 8601 format 形式で、以下の書式になる。 yyyy-mm-dd □ layout/main.xml --- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/date" /> </LinearLayout> --- □ values/strings.xml --- <resources> <string name="hello">Hello World, Dialog07Activity!</string> <string name="app_name">Dialog07</string> <string name="date_format">%tF</string> </resources> --- ■タイトルの形式をカスタマイズする 先のサンプル Dialog07 では、ダイアログのタイトルを表示する前に設定しても、日付を 変えると、タイトルの形式も変わってしまった。 タイトルの形式を変えないようにするには、DatePickerDialog を直接使うのではなく、 DatePicker をビューとしたカスタマイズのダイアログ(AlertDialog)として定義して使 う。 □ Dialog071Activity.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.widget.DatePicker; import android.widget.TextView; public class Dialog071Activity 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); DatePicker picker = new DatePicker(this); // (1) AlertDialog dialog = new AlertDialog.Builder(this) // (2) .setView(picker) // (3) .setTitle(String.format(getString(R.string.date_format), calendar)) // (4) .setPositiveButton(R.string.set_title, new DateSetHandler(picker, date)) // (5) .setNegativeButton(R.string.cancel_title, null) // (6) .show(); picker.init(year, monthOfYear, dayOfMonth, new DateChangedHandler(dialog)); // (7) Log.d("TEST", String.format("AlertDialog: %s", dialog.toString())); } private class DateChangedHandler implements DatePicker.OnDateChangedListener { // (8) private AlertDialog dialog; public DateChangedHandler(AlertDialog dialog) { this.dialog = dialog; } @Override public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) { // (9) Calendar calendar = Calendar.getInstance(); calendar.set(year, monthOfYear, dayOfMonth); Log.d("TEST", String.format("d: %tc", calendar)); dialog.setTitle(String.format(getString(R.string.date_format), calendar)); // (10) } } private class DateSetHandler implements DialogInterface.OnClickListener { // (11) private DatePicker picker; private TextView date; public DateSetHandler(DatePicker picker, TextView date) { this.picker = picker; this.date = date; } @Override public void onClick(DialogInterface dialog, int which) { // (12) Calendar calendar = Calendar.getInstance(); calendar.set(picker.getYear(), picker.getMonth(), picker.getDayOfMonth()); // (13) Log.d("TEST", String.format("c: %tc", calendar)); date.setText(String.format(getString(R.string.date_format), calendar)); } } } --- □ DatePicker picker = new DatePicker(this); // (1) 日付を選択するためのビューとして、DatePicker クラスを用いる。 ・クラス階層 java.lang.Object + android.view.View + android.view.ViewGroup + android.widget.FrameLayout + android.widget.DatePicker ・コンストラクター --- DatePicker(Context context) DatePicker(Context context, AttributeSet attrs) DatePicker(Context context, AttributeSet attrs, int defStyle) --- 自分自身の画面で使用するので、context は、自分自身(this)を用いる。 ・主なメソッド --- void init(int year, int monthOfYear, int dayOfMonth, DatePicker.OnDateChangedListener onDateChangedListener) --- □ AlertDialog dialog = new AlertDialog.Builder(this) // (2) DatePicker オブジェクトを乗せるための AlertDialog オブジェクトを生成する。 □ .setView(picker) // (3) カスタムビューとして、DatePicker オブジェクトを設定する。 □ .setTitle(String.format(getString(R.string.date_format),calendar)) // (4) ダイアログのタイトルを ISO 形式で設定する。リソースファイルで定義されているのは、 以下の形式である。 %tF □ .setPositiveButton(R.string.set_title, new DateSetHandler(picker, date)) // (5) 「設定ボタン」を設置する。ここでは、DialogInterface.OnClickListener を実装してい るオブジェクトをリスナーとして、ダイアログで設定した日付を表示するためのテキスト ビューを引数として渡している。 □ .setNegativeButton(R.string.cancel_title, null) // (6) 「キャンセル・ボタン」を設定する。 リスナー・オブジェクトは null になっているので、ボタンを押されたときは何もしない。 □ picker.init(year, monthOfYear, dayOfMonth, new DateChangedHandler(dialog)); // (7) DatePicker オブジェクトを初期化する。 引数は、最初に出したい日付(年・月・日)、日付が変化したときに動作するリスナーのオ ブジェクト。 今回は、日付が変わったときに、ダイアログのタイトルも変更したいので、リスナーには、 ダイアログのオブジェクトを渡している。 □ private class DateChangedHandler implements DatePicker.OnDateChangedListener { // (8) 引数は、最初に出したい日付(年・月・日)、日付が変化したときに動作するリスナーを定 義。このオブジェクトは、DatePicker.OnDateChangedListener インターフェースを実装 する。 DatePicker.OnDateChangedListener 日付の選択で、日付が変わったことに対するバンドラーを定義するリスナー・インターフ ェース。 ・メソッド --- abstract void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) --- 設定ボタンが押されたときは、これは動作しない。したがって、まったく日付を変える必 要がない場合は、設定ボタンのリスナーで、日付を設定する必要がある。 □ public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) { // (9) ダイアログで日付が変わったことに対するハンドラー・メソッド。 □ dialog.setTitle(String.format(getString(R.string.date_format), calendar)); // (10) ダイアログで日付が変わったら、その日付をタイトルに設定している。 タイトルの形式は、ISO 形式として、リソースに設定している。 □ private class DateSetHandler implements DialogInterface.OnClickListener { // (11) □ public void onClick(DialogInterface dialog, int which) { // (12) 設定ボタンがクリックされたことに対するリスナーのクラスとハンドラー・メソッドを定 義する。(「ダイアログ (1)」を参照) □ calendar.set(picker.getYear(), picker.getMonth(), picker.getDayOfMonth()); // (13) DatePicker オブジェクトを受け取っているので、そこから日付を取得して設定する。 □ values/strings.xml --- <resources> <string name="hello">Hello World, Dialog071Activity!</string> <string name="app_name">Dialog071</string> <string name="set_title">設定</string> <string name="cancel_title">キャンセル</string> <string name="date_format">%tF</string> </resources> --- 以上
練習問題(20110815版)解答例
================================================================================
■Q1. 身長(m)と体重(kg)を入力してBMIを求めて表示する。
BMIは次の式で求まる。
体重 / 身長^2
---
<html>
<head>
<title>JavaScript</title>
<style>
.question {background-color: #CCFFFF}
.answer {background-color: #CCFF99}
</style>
</head>
<body>
Q1. 身長(m)と体重(kg)を入力してBMIを求めて表示する。
BMIは次の式で求まる。
体重 / 身長2
<script type="text/javascript">
var height = Number(prompt("身長入力(m)", 0));
var weight = Number(prompt("体重入力(kg)", 0));
document.write(weight / (height * height) + "
");
</script>
</body>
</html>
---
■Q2. 2つの数値を入力する。
入力した2つの数値を、小さい順に並べて表示する。
---
<html>
<head>
<title>JavaScript</title>
<style>
.question {background-color: #CCFFFF}
.answer {background-color: #CCFF99}
</style>
</head>
<body>
Q2. 2つの数値を入力する。
入力した2つの数値を、小さい順に並べて表示する。
<script type="text/javascript">
var i1 = Number(prompt("1番目の数値を入力してください。", "0"));
var i2 = Number(prompt("2番目の数値を入力してください。", "0"));
document.write("入力した数値は " + i1 + ", " + i2);
document.write("
");
document.write("小さい順に並べると ... ");
if (i1
");
</script>
</body>
</html>
---
■Q3. 3つの数値を入力する。
入力した3つの数値の中の最小値を表示する。
---
<html>
<head>
<title>JavaScript</title>
<style>
.question {background-color: #CCFFFF}
.answer {background-color: #CCFF99}
</style>
</head>
<body>
Q3. 3つの数値を入力する。
入力した3つの数値の中の最小値を表示する。
<script type="text/javascript">
var i1 = Number(prompt("1番目の数値を入力してください。", "0"));
var i2 = Number(prompt("2番目の数値を入力してください。", "0"));
var i3 = Number(prompt("3番目の数値を入力してください。", "0"));
document.write("入力した数値は " + i1 + ", " + i2 + ", " + i3);
document.write("
");
document.write("最小値は ... ");
var min = i1;
if (min > i2) {
min = i2;
}
if (min > i3) {
min = i3;
}
document.write(min);
document.write("
");
</script>
</body>
</html>
---
■Q4. 数値を入力する。
入力した数値の符号を表示する。
表示は、「-」、「0」、「+」でかまわない。
---
<html>
<head>
<title>JavaScript</title>
<style>
.question {background-color: #CCFFFF}
.answer {background-color: #CCFF99}
</style>
</head>
<body>
Q4. 数値を入力する。
入力した数値の符号を表示する。
表示は、「-」、「0」、「+」でかまわない。
<script type="text/javascript">
var i1 = Number(prompt("数値を入力してください。", "0"));
document.write("入力した数値は " + i1);
document.write("
");
document.write("符号は ... ");
if (i1 < 0) {
document.write("「-」");
} else if (i1 == 0) {
document.write("「0」");
} else {
document.write("「+」");
}
document.write("<br/>");
</script>
</body>
</html>
---
■Q5. 数値を入力する。
入力した数値が、1~12の範囲に入っているかどうかを確認する。
表示は、「入っている」、「入っていない」でかまわない。
(ヒント) 範囲に入っていないものからチェックするとよい
---
<html>
<head>
<title>JavaScript</title>
<style>
.question {background-color: #CCFFFF}
.answer {background-color: #CCFF99}
</style>
</head>
<body>
Q5. 数値を入力する。
入力した数値が、1~12の範囲に入っているかどうかを確認する。
表示は、「入っている」、「入っていない」でかまわない。
(ヒント) 範囲に入っていないものからチェックするとよい
<script type="text/javascript">
var i1 = Number(prompt("数値を入力してください。", "0"));
document.write("入力した数値は " + i1);
document.write("
");
document.write("1~12の範囲に ... ");
if (i1 < 1) {
document.write("入っていない");
} else if (i1 > 12) {
document.write("入っていない");
} else {
document.write("入っている");
}
document.write("
");
</script>
</body>
</html>
---
■Q6. 3-for2.html で、年も選択できるようにする。
年の範囲は、1990年~2020年とする。
---
<html>
<head>
<title>JavaScript</title>
<style>
.question {background-color: #CCFFFF}
.answer {background-color: #CCFF99}
</style>
</head>
<body>
Q6. 3-for2.html で、年も選択できるようにする。
年の範囲は、1990年~2020年とする。
<form>
誕生日:
<select>
<script type="text/javascript">
for(var i = 1990; i <= 2020; i++){
document.write("<option>" + i + "</option>");
}
</script>
</select>
年
<select>
<script type="text/javascript">
for(var i = 1; i <= 12; i++){
document.write("<option>" + i + "</option>");
}
</script>
</select>
月
<select>
<script type="text/javascript">
for(var i = 1; i <= 31; i++){
document.write("<option>" + i + "</option>");
}
</script>
</select>
日
</form>
</script>
</body>
</html>
---
■Q7. 数値を入力する。
入力された数分、★を横に並べて表示する。
(ヒント) 1個の★を表示する文を繰り返す
---
<html>
<head>
<title>JavaScript</title>
<style>
.question {background-color: #CCFFFF}
.answer {background-color: #CCFF99}
</style>
</head>
<body>
Q7. 数値を入力する。
入力された数分、★を横に並べて表示する。
(ヒント) 1個の★を表示する文を繰り返す
<script type="text/javascript">
var i1 = Number(prompt("数値を入力してください。", "0"));
document.write("★を " + i1 + " 個並べる ...
");
for (var i = 1; i <= i1; i++) {
document.write("★");
}
</script>
</body>
</html>
---
■Q8. 1~100の間にある3の倍数を表示する。
(結果) 3 6 9 12 ... 99
(ヒント)
for文で、カウンターを更新する式を+3ずつにする
または
if文で、カウンターの値が3の倍数かどうかを判断して、3の倍数のときだけ表示する
なお、カウンターを3で割ったときの余りが0のとき、3の倍数である。
(応用)
3の倍数だけでなく、3のつく数字も表示する
---
<html>
<head>
<title>JavaScript</title>
<style>
.question {background-color: #CCFFFF}
.answer {background-color: #CCFF99}
</style>
</head>
<body>
Q8. 1~100の間にある3の倍数を表示する。
(結果) 3 6 9 12 ... 99
(ヒント)
for文で、カウンターを更新する式を+3ずつにする
または
if文で、カウンターの値が3の倍数かどうかを判断して、3の倍数のときだけ表示する
なお、カウンターを3で割ったときの余りが0のとき、3の倍数である。
(応用)
3の倍数だけでなく、3のつく数字も表示する
<script type="text/javascript">
for (var i = 3; i <= 100; i += 3) {
document.write(i);
document.write(" ");
}
</script>
別解
<script type="text/javascript">
for (var i = 1; i <= 100; i++) {
if ((i % 3) == 0) {
document.write(i);
document.write(" ");
}
}
</script>
応用
<script type="text/javascript">
for (var i = 1; i <= 100; i++) {
if ((i % 3) == 0) { // 3の倍数
document.write(i);
document.write(" ");
} else if ((i % 10) == 3) { // 1桁目が3
document.write(i);
document.write(" ");
} else if ((30 <= i) && (i <= 39)) { // 30台の数字
document.write(i);
document.write(" ");
}
}
</script>
</body>
</html>
---
■Q9. 「指定された数分、★を横に並べて表示する」関数 print_star(num) を作る。
この数は、promptを使って入力する。
(応用1)
この関数を利用して、★を正方形に並べる。
(結果例) 3つの場合
★★★
★★★
★★★
(応用2)
この関数を利用して、★を直角三角形に並べる。
(結果例) 3つの場合
★
★★
★★★
---
<html>
<head>
<title>JavaScript</title>
<style>
.question {background-color: #CCFFFF}
.answer {background-color: #CCFF99}
</style>
</head>
<script type="text/javascript">
function print_star(num) {
for (var i = 1; i <= num; i++) {
document.write("★");
}
}
</script>
<body>
Q9. 「指定された数分、★を横に並べて表示する」関数 print_star(num) を作る。
この数は、promptを使って入力する。
(応用1)
この関数を利用して、★を正方形に並べる。
(結果例) 3つの場合
★★★
★★★
★★★
(応用2)
この関数を利用して、★を直角三角形に並べる。
(結果例) 3つの場合
★
★★
★★★
<script type="text/javascript">
var i1 = Number(prompt("数値を入力してください。", "0"));
document.write("★を " + i1 + " 個並べる ...
");
print_star(i1);
</script>
(応用1)
<script type="text/javascript">
for (var i = 1; i
");
}
</script>
(応用2)
<script type="text/javascript">
for (var i = 1; i
");
}
</script>
</body>
</html>
---
練習問題(20110815版)
================================================================================
Q1. 身長(m)と体重(kg)を入力してBMIを求めて表示する。
BMIは次の式で求まる。
体重 / 身長^2
Q2. 2つの数値を入力する。
入力した2つの数値を、小さい順に並べて表示する。
Q3. 3つの数値を入力する。
入力した3つの数値の中の最小値を表示する。
Q4. 数値を入力する。
入力した数値の符号を表示する。
表示は、「-」、「0」、「+」でかまわない。
Q5. 数値を入力する。
入力した数値が、1~12の範囲に入っているかどうかを確認する。
表示は、「入っている」、「入っていない」でかまわない。
(ヒント) 範囲に入っていないものからチェックするとよい
Q6. 3-for2.html で、年も選択できるようにする。
年の範囲は、1990年~2020年とする。
Q7. 数値を入力する。
入力された数分、★を横に並べて表示する。
(ヒント) 1個の★を表示する文を繰り返す
Q8. 1~100の間にある3の倍数を表示する。
(結果) 3 6 9 12 ... 99
(ヒント)
for文で、カウンターを更新する式を+3ずつにする
または
if文で、カウンターの値が3の倍数かどうかを判断して、3の倍数のときだけ表示する
なお、カウンターを3で割ったときの余りが0のとき、3の倍数である。
(応用)
3の倍数だけでなく、3のつく数字も表示する
Q9. 「指定された数分、★を横に並べて表示する」関数 print_star(num) を作る。
この数は、promptを使って入力する。
(応用1)
この関数を利用して、★を正方形に並べる。
(結果例) 3つの場合
★★★
★★★
★★★
(応用2)
この関数を利用して、★を直角三角形に並べる。
(結果例) 3つの場合
★
★★
★★★
Android ダイアログ (4) プログレスダイアログ ================================================================================ Android 端末が時間のかかる処理を行っているときに、ユーザーが勘違いして戻るボタン を押したり、端末を再起動しないように、処理待ちのダイアログを表示させることができ る。 処理待ちのダイアログの表示は、本当に処理待ちだけを表すものと、進捗率を表示するも のの 2 種類ある。どちらも同じ ProgressDialog クラスのオブジェクトとして作る。 処理待ちのサンプル Dialog05 シンプルなもの Dialog051 キャンセルボタンつきのプログレスダイアログ Dialog052 プログレスダイアログと作業用のスレッドを別にする 進捗つきのプログレスダイアログ Dialog06 シンプルなもの Dialog061 プログレスダイアログと作業用のスレッドを別にする ■ 処理待ちのダイアログ まずは、処理待ちのダイアログ。 処理待ち、進捗つきのどちらにも共通するが、基本的な使い方は以下のとおり。 (1) オブジェクトを作って、 ProgressDialog progressDialog = new ProgressDialog(this); (2) スタイル(処理待ちか進捗つき)を設定して、 progressDialog.setProgressStyle(スタイル); (3) 表示する。 progressDialog.show(); そして、プログレスダイアログを表示している間に行う処理は、別スレッドとして定義し て、それを起動する。 Thread worker = new Thread(this); worker.start(); □ Dialog05Activity.java --- package jp.marunomaruno.android.sample; import android.app.Activity; import android.app.ProgressDialog; import android.os.Bundle; public class Dialog05Activity extends Activity implements Runnable { // (1) private ProgressDialog progressDialog; // (2) /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); progressDialog = new ProgressDialog(this); // (3) progressDialog.setTitle(R.string.dialogTitle); progressDialog.setMessage(getString(R.string.dialogMessage)); progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); // (4) // progressDialog.setCancelable(false); // デフォルトはtrue // (5) progressDialog.show(); Thread worker = new Thread(this); // (6) worker.start(); } public void run() { // (7) // ダイアログを出している間に行う処理 // (8) try { Thread.sleep(6 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } // ダイアログを消去する progressDialog.dismiss(); // (9) } } --- □ public class Dialog05Activity extends Activity implements Runnable { // (1) 処理待ちダイアログを表示するということは、ダイアログを表示している間に、別のスレ ッドが何かしらの作業をすることになる。その作業部分は、別スレッドで行うので、この サンプルでは、自分自身に Runnable インターフェースを実装し、run() メソッドを実装 して作る。 もちろん、別スレッドにするオブジェクトは、別クラスで作ることも可能である。 □ private ProgressDialog progressDialog; // (2) 作業しているスレッドと、この ProgressDialog オブジェクトを共有したいので、ここで はインスタンス変数として定義する。 ・ProgressDialogの階層 java.lang.Object + android.app.Dialog + android.app.AlertDialog + android.app.ProgressDialog ・おもなメソッド(おもに STYLE_SPINNER で使うかな、と思われるもの) --- void onStart() void setMessage(CharSequence message) void setProgressStyle(int style) --- ▼ ProgressDialog クラスには、staticなshow() メソッドがあるが、API 上にはなんの 記述もない。cancelListener が引数にないものはどうやって終わらせるのかな? スー パークラスを含めて、static メソッドはこれしかない。 □ progressDialog = new ProgressDialog(this); // (3) ProgressDialogのオブジェクトを生成する。 自分自身の画面に表示するので、context は自分自身(this)。 ・コンストラクター --- ProgressDialog(Context context) ProgressDialog(Context context, int theme) --- なお、ProgressDialog には、AlertDialog のように、Builder クラスがないので、メソ ッド・チェーンのようなことはできない。 □ progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); // (4) ダイアログの表示形式を指定する。表示形式は ProgressDialog クラスの定数として定義 してある。 --- int STYLE_HORIZONTAL 進捗バー int STYLE_SPINNER 処理待ち --- □ progressDialog.setCancelable(false); // デフォルトはtrue // (5) プログレスバーを表示しているときに、処理をキャンセルできるかどうかを指定する。 デフォルトは、「戻る」ボタンでキャンセルできる。キャンセルしたくない場合は、 setCancelable(false) で指定する。 □ Thread worker = new Thread(this); // (6) worker.start(); 処理待ちしている間の処理を記述するスレッドを起動する。 □ public void run() { // (7) 処理待ちしている間の処理を記述する。 □ // ダイアログを出している間に行う処理 // (8) ここに、ダイアログを出している間に行う処理を記述する。 今回は、単に5秒間スリープするだけ。 □ progressDialog.dismiss(); // (9) 処理が終わったら、ダイアログを消去する。 □ values/strings.xml --- <?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">Hello World, Dialog05Activity!</string> <string name="app_name">Dialog05Activity</string> <string name="dialogTitle">処理中...</string> <string name="dialogMessage">しばらくお待ちください</string> </resources> --- ■ キャンセルボタンつきのプログレスダイアログ デフォルトでは、「戻る」ボタンでキャンセルできる。 ただし、この操作を禁止して、ダイアログ上にキャンセルボタンを明示して、それを使わ せることもできる。 このサンプルでは、ボタンのリスナー、キャンセルされたときのリスナーを無名インナー クラスで実装している。 □ Dialog051Activity.java --- package jp.marunomaruno.android.sample; import android.app.Activity; import android.app.ProgressDialog; import android.content.DialogInterface; import android.os.Bundle; public class Dialog051Activity extends Activity implements Runnable { private ProgressDialog progressDialog; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final Thread worker = new Thread(this); // (1) progressDialog = new ProgressDialog(this); progressDialog.setTitle(R.string.dialogTitle); progressDialog.setMessage(getString(R.string.dialogMessage)); progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); progressDialog.setCancelable(false); // (2) progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, // (3) getString(R.string.cancelButtonTitle), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.cancel(); // ダイアログをキャンセルする } }); progressDialog .setOnCancelListener(new DialogInterface.OnCancelListener() { // (4) public void onCancel(DialogInterface dialog) { worker.stop(); // スレッドを停止する } }); progressDialog.show(); worker.start(); } public void run() { // ダイアログを出している間に行う処理 try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } progressDialog.dismiss(); // ダイアログを消去する } } --- □ final Thread worker = new Thread(this); // (1) 今回は無名インナークラスから、このスレッドのオブジェクトを使うので、final 指定す る。 □ progressDialog.setCancelable(false); // (2) プログレスバーを表示しているときに、処理をキャンセルできなくする。 こうすることで、自分で表示したボタンを使わせることができる。 □ progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, // (3) getString(R.string.cancelButtonTitle), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.cancel(); // ダイアログをキャンセルする } }); キャンセルボタンを設定する。 ・ボタンを設置するメソッド --- void setButton(int whichButton, CharSequence text, DialogInterface.OnClickListener listener) void setButton(int whichButton, CharSequence text, Message msg) --- 引数 int whichButton ボタンの種類。ボタンの種類は、DialogInterface インターフ ェースの定数 BUTTON_NEGATIVE、BUTTON_NEUTRAL、 BUTTON_POSITIVE を使う。 CharSequence text ボタンに表示する文字列 DialogInterface.OnClickListener listener ボタンをクリックしたときのリスナー Message msg ボタンをクリックしたときに送信するメッセージ ここでは、キャンセルボタンが押されたら、次のメソッドでダイアログをキャンセルする。 dialog.cancel(); // ダイアログをキャンセルする このメソッドにより、DialogInterface.OnCancelListener の onCancel() メソッドが動 作する。 なお、setPositiveButton() のようなメソッドは、AlertDialog.Builder クラスのメソッ ドで、ProgressDialog、AlertDialog、Dialog クラスにはない。したがって、 setButton() を使って、ボタンの種類も引数で指定する。 □ DialogInterface.OnClickListener ボタンがクリックされたときの動作を規定するリスナー。 宣言されているメソッドは、以下のもの。 abstract void onClick(DialogInterface dialog, int which) このメソッドは、AlertDialogのところで説明している。 □ progressDialog .setOnCancelListener(new DialogInterface.OnCancelListener() { // (4) public void onCancel(DialogInterface dialog) { worker.stop(); // スレッドを停止する } }); ダイアログがキャンセルされたときのリスナーを設定する。 ここでは、stop() メソッドにより、スレッドを停止させている。 □ DialogInterface.OnCancelListener ダイアログがキャンセルされたときのリスナー。 宣言されているメソッドは、以下のもの。 abstract void onCancel(DialogInterface dialog) □ values/strings.xml --- <?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">Hello World, Dialog051Activity!</string> <string name="app_name">Dialog051Activity</string> <string name="dialogTitle">処理中...</string> <string name="dialogMessage">しばらくお待ちください</string> <string name="cancelButtonTitle">キャンセル</string> </resources> --- ■ 作業用のスレッドを別クラスにする ここまでのサンプルは、作業用のスレッドの中に、次のようなプログレスダイアログを直 接制御する文が入っていた。 progressDialog.dismiss(); // ダイアログを消去する ただ、これはあまり望ましいことではない。 このサンプルは、直接、プログレスダイアログを制御するのではなく、間接的にプログレ スダイアログを制御するようにする。 そのために、Android では、メッセージというものを使って、スレッド間で制御ができる ようにしている。 □ Dialog052Activity.java --- package jp.marunomaruno.android.sample; import android.app.Activity; import android.app.ProgressDialog; import android.os.Bundle; import android.os.Handler; import android.os.Message; public class Dialog052Activity extends Activity { private ProgressDialog progressDialog; private Handler progressDialogHandler; // (1) /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); progressDialogHandler = new PprogressDialogHandler(); // (2) progressDialog = new ProgressDialog(this); progressDialog.setTitle(R.string.dialogTitle); progressDialog.setMessage(getString(R.string.dialogMessage)); progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); progressDialog.show(); Thread worker = new Thread(new WorkerThread()); // (3) worker.start(); } /** * メッセージを受け取ったときの動きを規定するハンドラー */ private class PprogressDialogHandler extends Handler { // (4) @Override public void handleMessage(Message msg) { // (5) // プログレスダイアログを終了する progressDialog.dismiss(); progressDialog = null; } } /** * 作業をするスレッド */ private class WorkerThread implements Runnable { public void run() { try { Thread.sleep(6 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } // 作業終了時に、プログレスダイアログを終了するようにハンドラーを呼ぶ progressDialogHandler.sendEmptyMessage(-1); // (6) } } } --- □ private Handler progressDialogHandler; // (1) Handler クラスのインスタンス変数を宣言する。 java.lang.Object + android.os.Handler Handler クラスは、マルチスレッドで、スレッド間でメッセージをやり取りするのを実現 するためのクラス。 詳細は、以下のURLを参照のこと。 Android の Handler とは何か? http://www.adamrocker.com/blog/261/what-is-the-handler-in-android.html --- ・AndroidのUI操作はシングル・スレッド モデル ・ユーザビリティ向上の為にはマルチスレッドが必要 ・Handlerで実現 ・Handlerを使わない場合に起きる例外は実行スレッドのチェックで発生 ・Handlerを使うと、UI Threadの持つキューにジョブを登録できる ・キューはUI Threadにより実行される ・別スレッドからUI Threadに処理を登録するのでスレッドチェックで例外が発生しない --- ・メッセージを送るメソッド --- final boolean post(Runnable r) final boolean postAtFrontOfQueue(Runnable r) final boolean postAtTime(Runnable r, Object token, long uptimeMillis) final boolean postAtTime(Runnable r, long uptimeMillis) final boolean postDelayed(Runnable r, long delayMillis) final boolean sendEmptyMessage(int what) final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) final boolean sendEmptyMessageDelayed(int what, long delayMillis) final boolean sendMessage(Message msg) final boolean sendMessageAtFrontOfQueue(Message msg) boolean sendMessageAtTime(Message msg, long uptimeMillis) final boolean sendMessageDelayed(Message msg, long delayMillis) --- ・メッセージを受け取るメソッド --- void handleMessage(Message msg) --- □ progressDialogHandler = new PprogressDialogHandler(); // (2) □ private class PprogressDialogHandler extends Handler { // (4) □ public void handleMessage(Message msg) { // (5) メッセージを受け取ったときの動きを規定するハンドラー・クラス(内部クラス)のオブジ ェクトを生成する。 そのクラスは、Handlerクラスのサブクラスとして作る。 オーバーライドするメソッドは、つぎのメソッド。 void handleMessage(Message msg) このメソッドで、メッセージを受け取る。 今回は、プログレスダイアログを終了している。 □ Thread worker = new Thread(new WorkerThread()); // (3) □ progressDialogHandler.sendEmptyMessage(-1); // (6) 作業用のスレッドのオブジェクトを生成する。 作業用スレッドでは、単純に、作業だけのことを記述している。 作業が終わったときに、終了したよ、というメッセージをハンドラーに送信しているだけ なので、このスレッドでは、プログレスダイアログについてはなにも知らなくてよい。 今回は、sendEmptyMessage(-1) として送信している。 引数としての -1 は、任意の値。この値は、Handler.handleMessage(Message msg) の引 数のMessageオブジェクトのインスタンス変数 what に格納されている。 Messageクラスについては、もう少し後 (Dialog061 アプリ)で記する。 ■ 進捗つきのプログレスダイアログ プログレスダイアログとして、プログレスバーで進捗率がわかるもののサンプル。 作業と、プログレスバーの進捗率の設定が分かれていないバージョン。 ProgressDialog クラスを使うのは一緒で、そのスタイルを、STYLE_SPINNER ではなく、 STYLE_HORIZONTAL にすることで実現する。 なお、進捗率は、setProgress() メソッドで指定する。 このサンプルは、0.5 秒ごとに進捗率を 10% ずつ上げていく。5 秒で 100% となって終 了する。 □ Dialog06Activity.java --- package jp.marunomaruno.android.sample; import android.app.Activity; import android.app.ProgressDialog; import android.os.Bundle; public class Dialog06Activity extends Activity { private ProgressDialog progressDialog; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); progressDialog = new ProgressDialog(this); progressDialog.setTitle(R.string.dialogTitle); progressDialog.setMessage(getString(R.string.dialogMessage)); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); // (1) progressDialog.show(); new Thread(new WorkerThread()).start(); // (2) } /** * 作業しながらプログレスバーを出すスレッド */ private class WorkerThread implements Runnable { public void run() { try { for (int i = 0; i <= 10; i++) { Thread.sleep(500); // 0.5秒 progressDialog.setProgress(i * 10); // (3) } } catch (InterruptedException e) { e.printStackTrace(); } progressDialog.dismiss(); } } } --- □ progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); // (1) ProgressDialog クラスのおもなメソッド(おもに STYLE_HORIZONTAL で使うかな、と思 われるもの) --- int getMax() void incrementProgressBy(int diff) void setMax(int max) void setMessage(CharSequence message) void setProgress(int value) void setProgressNumberFormat(String format) void setProgressPercentFormat(NumberFormat format) --- 進捗率の最大値は、setMax() で指定する。0 から 10000 まで指定できる。デフォルトの 最大値は 100。 これに合わせて、setProgress() で現在の進捗率を指定すると、プログレスバーの進捗率 が、% で表示される。 □ new Thread(new WorkerThread()).start(); // (2) 作業用のスレッドを生成して起動。 □ progressDialog.setProgress(i * 10); // (3) 0%、10%、20%、...、100% までの進捗率なので、その値を設定する。 デフォルトの最大値が100なので、そのままの%の値を指定すればよい。 ■ 作業用のスレッドと別のスレッドでプログレスバーを表示する Handlerクラスを使って、進捗率を作業用スレッドとは独立に管理するバージョン。 □ Dialog061Activity.java --- package jp.marunomaruno.android.sample; import android.app.Activity; import android.app.ProgressDialog; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; public class Dialog061Activity extends Activity { private ProgressDialog progressDialog; private Handler progressDialogHandler; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); progressDialogHandler = new PprogressDialogHandler(); progressDialog = new ProgressDialog(this); progressDialog.setTitle(R.string.dialogTitle); progressDialog.setMessage(getString(R.string.dialogMessage)); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.show(); progressDialog.setProgress(0); // (1) Thread worker = new Thread(new WorkerThread()); worker.start(); } /** * メッセージを受け取ったときの動きを規定するハンドラー */ private class PprogressDialogHandler extends Handler { @Override public void handleMessage(Message msg) { Log.d("TEST", msg.toString()); // 値が非負なら、プログレスバーを更新する if (msg.what >= 0) { // (2) progressDialog.setProgress(msg.what); // (3) return; } // プログレスダイアログを終了する progressDialog.setProgress(100); // (4) progressDialog.dismiss(); progressDialog = null; } } /** * 作業をするスレッド */ private class WorkerThread implements Runnable { public void run() { try { for (int i = 0; i <= 10; i++) { Thread.sleep(500); // 0.5秒 Message msg = new Message(); // (5) msg.what = 0; // (6) msg.arg1 = i * 10; // (7) progressDialogHandler.sendMessage(msg); // (8) } } catch (InterruptedException e) { e.printStackTrace(); } // 作業終了時に、プログレスダイアログを終了するようにハンドラーを呼ぶ progressDialogHandler.sendEmptyMessage(-1); // (9) } } } --- □ progressDialog.setProgress(0); // (1) プログレスバーを表示する際に、進捗率を 0% にしておく。 □ if (msg.what >= 0) { // (2) □ progressDialog.setProgress(msg.what); // (3) 受け取ったメッセージを処理する。 void handleMessage(Message msg) 今回は、メッセージに従って、進捗率を指定したり、プログレスダイアログを終了したり している。 handleMessage(Message msg) メソッドの引数 msg のインスタンス変数 what に、sendEm ptyMessage() メソッドで指定した値が入っている。 値が 0 以上のときは、進捗率、負数のときは、ダイアログを終了するようにしている。 □ Message クラス java.lang.Object + android.os.Message Handlerクラスを使ってメッセージを送信するときのデータのオブジェクトの型。 つぎのインスタンス変数を使ってデータを送る。 int 型のデータひとつかふたつであれば、arg1、arg2 を使えばよい。それ以外は、オブ ジェクトを使う。 メッセージの種類は what を使う。 ・おもな public インスタンス変数 --- public int arg1 データ public int arg2 データ public Object obj データ public int what メッセージの種類 --- □ progressDialog.setProgress(100); // (4) 処理が終わったときは、進捗率を 100 にする。 □ Message msg = new Message(); // (5) □ msg.what = 0; // (6) □ msg.arg1 = i * 10; // (7) Messageオブジェクトに、進捗率を設定して、ハンドラーに送信する。 進捗率は、0~100 までの値なので、arg1 に設定しておけばよい。 送りたいデータが2つのint値で済まないような場合は、obj を使う。 したがって、long値を送りたい場合も、Long型のオブジェクトを作って送る必要がある。 □ progressDialogHandler.sendMessage(msg); // (8) Message オブジェクトを送るので、メソッドは final boolean sendMessage(Message msg) を使う。 □ progressDialogHandler.sendEmptyMessage(-1); // (9) 処理が終わったときは、what を -1 にして送る。 ここでは、what だけでよいので、sendEmptyMessage() でかまわない。 以上
ダイアログ (3) テキスト入力 ================================================================================ ■ テキスト入力 ダイアログを使って、テキストを入力することもできる。 これも、AlertDialogを使う。実際には、テキスト入力だけではなく、カスタムダイアロ グとして、いろいろと使える。 □ Dialog04Activity.java --- package jp.marunomaruno.android.sample; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.os.Bundle; import android.widget.EditText; import android.widget.TextView; public class Dialog04Activity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final TextView textView = (TextView) findViewById(R.id.name); final EditText editText = new EditText(this); // (1) new AlertDialog.Builder(this) .setTitle(R.string.alertDialogTitle) .setMessage(R.string.alertDialogMessage) .setPositiveButton(R.string.alertDialogPositiveButton, new OKButtonClickHandler(textView, editText)) .setView(editText) // (2) .show(); } /** * OKボタンをクリックしたときのハンドラー */ private class OKButtonClickHandler implements OnClickListener { TextView textView; EditText editText; public OKButtonClickHandler(TextView textView, EditText editText) { this.textView = textView; this.editText = editText; } @Override public void onClick(DialogInterface dialog, int which) { textView.setText(String .format(getString(R.string.alertDialogResultFormat), // (3) editText.getText())); } } } --- (1) android.widget.EditText テキストを入力するビューのクラス。 主なメソッド CharSequence getText() void setText(int resid) void setText(char[] text, int start, int len) void setText(int resid, TextView.BufferType type) void setText(CharSequence text) void setText(CharSequence text, TextView.BufferType type) 今回は使っていないが、テキストの範囲選択関係のメソッド。これらは、 android.text.Selection クラスのメソッドを使いやすくしたものとして定義されている。 void extendSelection(int index) カーソルの現在位置からindexまで選択 void selectAll() すべて選択 void setSelection(int start, int stop) startからstopまで選択 カーソルの移動メソッド void setSelection(int index) indexに移動 android.widget.TextView.BufferType enumとして定義されている。 TextView.BufferType EDITABLE TextView.BufferType NORMAL TextView.BufferType SPANNABLE (2) setView(editText) 編集ビューをダイアログに設定する。 (3) OKボタンをクリックしたら、編集ビューの値(テキスト)を取得して、それをもとにテ キスト・ビューにその値を設定する。 実際には、String.format()メソッドのフォーマットは、リソースで定義している "%s さ ん、こんにちは!" という形式。%s のところに、editText.getText() で値を持ってきて いる。 □ res/layout/main.xml --- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/name" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout> --- □ res/values/strings.xml --- <resources> <string name="alertDialogTitle">名前入力</string> <string name="alertDialogMessage">名前を入力してください</string> <string name="alertDialogPositiveButton">OK</string> <string name="alertDialogResultFormat">%s さん、こんにちは!</string> <string name="app_name">Dialog04</string> </resources> --- ■ 最初からソフトキーボードを表示 Dialog04のサンプルでは、入力エリアをクリックしないと、ソフトキーボードは表示され ませんでした。 最初からソフトキーボードが表示されいた方がよいこともあります。 このサンプルでは、最初からソフトキーボードを表示するようにします。 □ Dialog041Activity.java --- package jp.marunomaruno.android.sample; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.os.Bundle; import android.view.View; import android.view.WindowManager; import android.view.View.OnFocusChangeListener; import android.widget.EditText; import android.widget.TextView; public class Dialog041Activity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final TextView textView = (TextView) findViewById(R.id.name); final EditText editText = new EditText(this); final AlertDialog alertDialog = new AlertDialog.Builder(this) .setTitle(R.string.alertDialogTitle) .setMessage(R.string.alertDialogMessage) .setPositiveButton(R.string.alertDialogPositiveButton, new OKButtonClickHandler(textView, editText)) .setView(editText) .create(); editText.setOnFocusChangeListener(new ForcusChangeHandler(alertDialog));// (1) alertDialog.show(); } /** * フォーカスが変化したときのハンドラー */ private class ForcusChangeHandler implements OnFocusChangeListener { // (2) AlertDialog dialog; public ForcusChangeHandler(AlertDialog dialog) { this.dialog = dialog; } @Override public void onFocusChange(View v, boolean hasFocus) { // (3) if (hasFocus) { dialog.getWindow().setSoftInputMode( // (4) WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); } } } /** * OKボタンをクリックしたときのハンドラー */ private class OKButtonClickHandler implements OnClickListener { TextView textView; EditText editText; public OKButtonClickHandler(TextView textView, EditText editText) { this.textView = textView; this.editText = editText; } @Override public void onClick(DialogInterface dialog, int which) { textView.setText(String .format(getString(R.string.alertDialogResultFormat), editText.getText())); } } } --- (1) setOnFocusChangeListener View クラスのメソッド。 public void setOnFocusChangeListener (View.OnFocusChangeListener l) (2) android.view.View.OnFocusChangeListener (3) void onFocusChange(View v, boolean hasFocus) ビューのフォーカスが変わったことを検知するリスナーのインターフェースと、そのハン ドラーのメソッド。 (4) dialog.getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); ダイアログのウィンドウに対して、softInputModeを設定する。設定する値は、 WindowManager.LayoutParamsクラスのSOFT_INPUT_STATE_xxx定数で指定する。 今回は、ダイアログがフォーカスを受けたときには常にソフトキーボ-ドを表示したいの で、SOFT_INPUT_STATE_ALWAYS_VISIBLE を指定する。 これが、SOFT_INPUT_STATE_VISIBLE だと、ダイアログがたとえ表示されていたとしても、 自分でフォーカスを当てない限り、ソフトキーボードが表示されない。 WindowManager.LayoutParams ソフトウェア入力の状態を示す定数には以下のものがある。 int SOFT_INPUT_STATE_ALWAYS_HIDDEN ウィンドウがフォーカスを受けたときに常に soft input area を隠す int SOFT_INPUT_STATE_ALWAYS_VISIBLE ウィンドウがフォーカスを受けたときに常に soft input area を表示する int SOFT_INPUT_STATE_HIDDEN 通常、適切なとき(ユーザがウィンドウを前面に持ってきたとき)に soft input area を隠す int SOFT_INPUT_STATE_UNCHANGED soft input area の状態を変更しない int SOFT_INPUT_STATE_UNSPECIFIED 状態は指定されていない int SOFT_INPUT_STATE_VISIBLE 通常、適切なとき(ユーザがウィンドウを前面に持ってきたとき)に soft input area を表示する 上記以外にも、いろいろな種類の定数が定義されている。また、これらは次のフィールド の値として管理されている。 public int softInputMode 関連するものとしては、 SOFT_INPUT_ADJUST_UNSPECIFIED SOFT_INPUT_ADJUST_RESIZE SOFT_INPUT_ADJUST_PAN などがあり、値を取り出すための SOFT_INPUT_IS_FORWARD_NAVIGATION SOFT_INPUT_MASK_ADJUST SOFT_INPUT_MASK_STATE SOFT_INPUT_MODE_CHANGED などのマスクが用意されている。