Sensorオブジェクトを取得するには、SensorManagerオブジェクトのgetDefaultSensor()メソッドもしくはgetSensorList()メソッドを使用します。
getDefaultSensor()メソッドの引数にSensorクラスに定義されているTYPE_***定数を使用することで、指定した種類のSensorオブジェクトを取得することができます。以下の例では、MAGNETIC_FIELDすなわち地磁気センサーを指定しています。
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
また、getSensorList()メソッドの場合も同じように引数にTYPE_***定数を使用します。
List<Sensor> deviceSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
メソッドの戻り値のデータ型に注目してください。getSensorList()メソッドの戻り値はListクラスとなっています。引数にTYPE_ALLを渡すことでそのAndroid端末で使用可能な全てのセンサーを取得することが可能です。
Sensorクラスは各種センサー機器に共通する属性(名前、センサー種類、開発ベンダー、バージョン番号等)を管理するクラスでした。
このクラスには以下のようなメソッドが存在し、センサーに関する情報を取得することができます。
メソッドのシグネチャ | 処理内容 |
---|---|
public String getName() | センサーの名前を取得する |
public int getType() | センサーのタイプを取得する |
public String getVendor() | センサーのベンダー名を取得する |
public int getVersion() | センサーのバージョンを取得する |
次にSensorクラスに定義されている代表的な定数を紹介します。
各プラットフォーム毎に利用可能な(サポートされるようになった)センサーが違っているというところに注目してください。
非推奨となっているセンサーは下位互換のために存在しているため、基本的には使用しないことが推奨されています。
定数名 | 説明 | Android 4.0 | Android 2.3 | Android 2.2 | Android 1.5 |
---|---|---|---|---|---|
TYPE_ACCELEROMETER | 加速度 | ○ | ○ | ○ | ○ |
TYPE_AMBIENT_TEMPERATURE | 周囲温度 | ○ | |||
TYPE_GRAVITY | 重力 | ○ | ○ | ||
TYPE_GYROSCOPE | ジャイロスコープ | ○ | ○ | ||
TYPE_LIGHT | 照度 | ○ | ○ | ○ | ○ |
TYPE_LINEAR_ACCELERATION | 線形加速度 | ○ | ○ | ||
TYPE_MAGNETIC_FIELD | 地磁気 | ○ | ○ | ○ | ○ |
TYPE_ORIENTATION | 傾き | 非推奨 | 非推奨 | 非推奨 | ○ |
TYPE_PRESSURE | 気圧 | ○ | ○ | ||
TYPE_PROXIMITY | 近接 | ○ | ○ | ○ | ○ |
TYPE_RELATIVE_HUMIDITY | 相対温度 | ○ | |||
TYPE_ROTATION_VECTOR | 回転ベクトル | ○ | ○ | ||
TYPE_TEMPERATURE | 温度 | 非推奨 | ○ | ○ | ○ |
SensorEventListenerインタフェースにはコールバックメソッドとして、2つのメソッドonAccuracyChanged()メソッドとonSensorChanged()メソッドが定義されています。
onAccuracyChanged()メソッドのシグネチャは以下の通りです。このメソッドはセンサーの精度が変化したときに呼ばれるコールバックメソッドです。
public void onAccuracyChanged (Sensor sensor, int accuracy)
onSensorChanged()メソッドのシグネチャは以下の通りです。このメソッドはセンサーの値が変化したときに呼ばれるコールバックメソッドです。
public void onSensorChanged (SensorEvent event)
SensorEventListenerインタフェースを実装したクラスを作成し、これら2つのメソッドをオーバーライドします。
public class MySensorEventListener implements SensorEventListener { @Override public final void onAccuracyChanged(Sensor sensor, int accuracy) { // センサーの精度が変更されると呼ばれる } @Override public final void onSensorChanged(SensorEvent event) { // センサーの値が変化すると呼ばれる } }
あとは、作成したクラス(MySensorEventListenerクラス)をインスタンス化し、SensorManagerクラスに定義されているregisterListener()メソッドを呼び出すことでイベントリスナーの登録を行います。
private Sensor light; private SensorEventListener listener; // …省略… listener = new MySensorEventListener(); sensorManager.registerListener(listener, light, SensorManager.SENSOR_DELAY_NORMAL);
registerListener()メソッドは3つの引数をとるメソッドであり、第1引数にはSensorEventListenerインタフェースを実装したクラスのオブジェクト、第2引数にはSensorオブジェクト、第3引数にはint型の値を渡す必要があります。
上記例では、第1引数には先ほど作成したMySensorEventListenerクラスのオブジェクトを、第2引数にはあらかじめ取得しておいた照度センサーのオブジェクトを表すlight変数を、第3引数にはSensorManagerクラスに定義されているSENSOR_DELAY_NORMAL定数を渡しています。
第3引数は、センサーの反応速度を決めるための値です。
SensorManagerクラスに定義されている定数を用いることが一般的で、以下のような定数が用意されています。
定数名 | 説明 | 遅延 |
---|---|---|
SENSOR_DELAY_FASTEST | ゲームよりもさらに素早く反応させたい場合に指定する | 0ms程度 |
SENSOR_DELAY_GAME | ゲーム用途で使用する場合に指定する | 20ms程度 |
SENSOR_DELAY_NORMAL | デフォルト | 60ms程度 |
SENSOR_DELAY_UI | ユーザーインタフェース用途で使用する場合に指定する | 200ms程度 |
反応速度を早くすると、その分だけバッテリーの消費が多くなってしまいます。不必要に早い反応速度にしないようにすることが大切です。
SensorEventクラスには以下の情報が属性として含まれています。
アクセス修飾子 | データ型 | 属性名 | 概要 |
---|---|---|---|
public | int | accuracy | イベントの精度 |
public | Sensor | sensor | イベントが発生したセンサー |
public | long | timestamp | イベントが発生した時間 |
public | float[] | values | センサーが取得した値 |
これらの属性はすべてpublic宣言されているため、メソッドを介す必要がなく直接参照することができます。
values属性はセンサーで検出した値が格納されている属性です。
データ型が配列となっている点がポイントであり、「値がどのように格納されるか」はセンサーの種類によって異なります。
例えば、TYPE_ACCELEROMETER(加速度センサー)やTYPE_MAGNETIC_FIELD(地磁気センサー)は3次元ベクトルの値を計測するセンサーであるため、測定した値を以下のようにX、Y、Z軸の要素に分割し、values[0]からvalues[2]に保持します。
一方、TYPE_LIGHT(照度センサー)は計測する値は明るさという単一のデータのみなので、values[0]しか使用しません。
TYPE_ACCELEROMETER | TYPE_LIGHT | TYPE_MAGNETIC_FIELD | |
---|---|---|---|
values[0] | X軸の加速度(m/s^2) | 明るさ(ルクス) | X軸の地磁気(uT) |
values[1] | Y軸の加速度(m/s^2) | 使用しない | Y軸の地磁気(uT) |
values[2] | Z軸の加速度(m/s^2) | 使用しない | Z軸の地磁気(uT) |
各センサーで計測した値の単位やどのようにvalues[]に格納されるかは公式HPを参照して下さい。
http://developer.android.com/reference/android/hardware/SensorEvent.html
なお、Android端末におけるX軸、Y軸、Z軸は以下のように定義されています。
センサーが動き続けるとバッテリーを消費してしまうため、適宜(Activityの場合であれば、onPause()メソッド等)イベントリスナーの解除を行うことが重要です。
リスナーの解除にはSensorManagerクラスに定義されているunregisterListener()メソッドを使用します。
@Override protected void onPause() { super.onPause(); sensorManager.unregisterListener(listener); }
ListViewのアイテムをクリックすると、そのセンサーで取得している値をライブ表示します。
まずは、MainActivityのレイアウトXMLファイルです。
● res/layout/activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" > <ListView android:id="@+id/listView1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" > </ListView> </RelativeLayout>
ListViewを使用するため、RelaytiveLayout内に定義しました。
次に、MainActivityのソースプログラムです。
● src/com.example.sensorsample/MainActivity.java
package com.example.sensorsample; // ...省略... public class MainActivity extends Activity implements OnItemClickListener{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL); ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, sensors){ @Override public Object getItem(int position) { Sensor sensor = (Sensor)super.getItem(position); return sensor.getName(); } }; ListView listView = (ListView) findViewById(R.id.listView1); listView.setAdapter(adapter); listView.setOnItemClickListener(this); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Intent intent = new Intent(this, DetailActivity.class); intent.putExtra("ID", position); startActivity(intent); } }
10行目でSensorManagerオブジェクトの取得、11行目で全SensorオブジェクトをListで取得しています。
13行目から21行目で取得したListを使い、ArrayAdapterを作成しています。
各アイテムのレイアウトにはsimple_list_item_1を使用しました。
ArrayAdapterを作成する際に、匿名クラスでgetItem()メソッドをオーバーライドしています。
このメソッドは、ListViewの各アイテムの内容を表示する際に使われるメソッドで、オーバーライドしていない(デフォルトでは)とSensorクラスのtoString()メソッドの実行結果が使われてしまいます。これでは、何のセンサーだかわかりませんので、ListViewに表示する文字をSensorオブジェクトの名前に変更しています。
あとは23行目から25行目でListViewオブジェクトの生成、Adapterの設定、ItemClickListenerインタフェースの設定を行いました。
29行目からはonItemClick()メソッドをオーバーライドしています。
ListViewの要素をクリックすると、何番目がクリックされたのか(position)を付加情報に設定したIntentを作成し、DetailActivityを起動します。
次に、DetailActivityのレイアウトXMLファイルです。このActivityはListViewをクリックした際に表示する2つ目のActivityです。
● res/layout/activity_detail.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical"> <TextView android:id="@+id/textView1" android:textStyle="bold" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/textView2" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/textView3" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/textView4" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
LinearLayoutの中に4つのTextViewを縦に並べました。
1つ目のTextViewにはセンサー名を、2~4つ目のTextViewはそれぞれX軸、Y軸、Z軸の値を表示するために用います。
次に、DetailActivityのソースプログラムです。
● src/com.example.sensorsample/DetailActivity.java
package com.example.sensorsample; // ...省略... public class DetailActivity extends Activity { private SensorManager sensorManager = null; private List<Sensor> sensors = null; private SensorEventListener listener = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_detail); sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensors = sensorManager.getSensorList(Sensor.TYPE_ALL); final TextView x = (TextView) findViewById(R.id.textView2); final TextView y = (TextView) findViewById(R.id.textView3); final TextView z = (TextView) findViewById(R.id.textView4); listener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { x.setText("x:" + String.valueOf(event.values[0])); y.setText("y:" + String.valueOf(event.values[1])); z.setText("z:" + String.valueOf(event.values[2])); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } }; } @Override protected void onResume() { super.onResume(); int id = getIntent().getIntExtra("ID", 0); Sensor sensor = sensors.get(id); TextView title = (TextView) findViewById(R.id.textView1); title.setText(sensor.getName()); sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL); } @Override protected void onPause() { super.onPause(); sensorManager.unregisterListener(listener); } }
20から31行目で、匿名クラスを使用しSensorEventListenerインタフェースをインスタンス化しています。
onAccuracyChanged()メソッドは空(何も実装していない)ですが、onSensorChanged()メソッドではセンサーから取得した値をTextViewに設定しています。
また、35行目からはonResume()メソッドをオーバーライドしています。
Intentの付加情報に設定されているIDを取り出し、全Sensorリストの中からリスニングするSensorオブジェクトを特定します。
あとは、44行目でSensorEventListenerの設定を行い、リスニングを開始しています。
48行目からはonPause()メソッドをオーバーライドしています。
この中では設定されているイベントリスナーの解除を行いっています。つまり、Activityがバックグラウンドに移ったときにリスニングの解除をしていることになります。
最後にマニフェストファイルです。
● AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.sensorsample" ...省略...> <application ...省略...> <activity android:name="com.example.sensorsample.DetailActivity" /> </application> </manifest>
DetailActivityを新しく追加しているので、マニフェストファイルで追加しているのは8行目だけです。