goo blog サービス終了のお知らせ 

marunomaruno-memo

marunomaruno-memo

[Android] センサー (2) いろいろなセンサー

2011年09月17日 | Android
センサー (2) いろいろなセンサー
================================================================================

■ 各種センサーの値を取得する

Android2.2 と DELL の実機で使えるセンサーをすべて確認する。これは、センサー(1) 
http://blog.goo.ne.jp/marunomarunogoo/d/20110828 
の「○」がついているセンサーの値を取得して表示するアプリケーション。

ソースは以下のとおり。
SensorValue01Activity.java    
    各種センサーの値を表示するアクティビティ・クラス

SensorEventListenerAdapter.java
    TextViewを保持するセンサー・リスナーのアダプタークラス
OneDimensionalSensorListenerAdapter.java    
    1 次元の値を取得するセンサー・リスナーのアダプタークラス
ThreeDimensionalSensorListenerAdapter.java    
    3 次元の値を取得するセンサー・リスナーのアダプタークラス

AcceleraterSensorListener.java      加速度センサーリスナー       3 次元値
GyroscopeSensorListener.java        ジャイロ・センサー・リスナー   3 次元値
LightSensorListener.java            光センサー・リスナー          1 次元値
MagneticFieldSensorListener.java    地磁気センサー・リスナー      3 次元値
PressureSensorListener.java         圧力センサー・リスナー        1 次元値
ProximitySensorListener.java        近接センサー・リスナー        1 次元値
TemperatureSensorListener.java      温度センサー・リスナー        1 次元値


少し複雑な階層の関係になっているが、基本は、1 次元の値を取得するセンサー系と、3 
次元の値を取得するセンサー系に分かれる。これらに対するリスナーのアダプタークラス
を用意して、それぞれのセンサー・リスナーは、このアダプター・クラスのサブクラスとし
て作る。なお、1次元系と3次元系もほぼ同じ形式になっているので、このスーパークラス
 SensorEventListenerAdapter を準備する。


◆ アクティビティのクラス。

□ SensorValue01Activity.java
---
package jp.marunomaruno.android.sample;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.TextView;

/**
 * Android2.2でサポートされているセンサーの状態を表示する。
 * @author marunomaruno
 * @version 1.0, 2011-06-25
 */
public class SensorValue01Activity extends Activity {

    // TextView
    private TextView acceleraterTextView; // 加速度センサー   // (1)
    private TextView gravityTextView; // 重力センサー
    private TextView gyroscopeTextView; // ジャイロスコープセンサー
    private TextView lightTextView; // 光センサー
    private TextView linearAccelerationTextView; // 線形加速度センサー
    private TextView magneticFieldTextView; // 地磁気センサー
    private TextView pressureTextView; // 圧力センサー
    private TextView proximityTextView; // 近接センサー
    private TextView rotationVectorTextView; // 回転ベクトルセンサー
    private TextView temperatureTextView; // 温度センサー

    private SensorManager sensorManager;                               // (2)
    private List<SensorEventListener> sensorEventListenerList; 
            // onPause()が呼ばれたときに、すべてのリスナーを解除するため // (3)

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        acceleraterTextView = (TextView) findViewById(R.id.accelerater); // (4)
        gravityTextView = (TextView) findViewById(R.id.gravity);
        gyroscopeTextView = (TextView) findViewById(R.id.gyroscope);
        lightTextView = (TextView) findViewById(R.id.light);
        linearAccelerationTextView = (TextView) findViewById(R.id.linearAcceleration);
        magneticFieldTextView = (TextView) findViewById(R.id.magneticField);
        pressureTextView = (TextView) findViewById(R.id.pressure);
        proximityTextView = (TextView) findViewById(R.id.proximity);
        rotationVectorTextView = (TextView) findViewById(R.id.rotationVector);
        temperatureTextView = (TextView) findViewById(R.id.temperature);

        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);// (5)
        sensorEventListenerList = new ArrayList<SensorEventListener>();  // (6)
    }

    @Override
    protected void onResume() {
        super.onResume();

        // 加速度センサーを取得して登録する
        getSensorAndAddToListener(Sensor.TYPE_ACCELEROMETER,
                new AcceleraterSensorListener(acceleraterTextView),
                R.string.accelerater);                                  // (7)

        // ジャイロスコープ・センサーを取得して登録する
        getSensorAndAddToListener(Sensor.TYPE_GYROSCOPE,
                new GyroscopeSensorListener(gyroscopeTextView),
                R.string.gyroscope);

        // 光センサーを取得して登録する
        getSensorAndAddToListener(Sensor.TYPE_LIGHT, new LightSensorListener(
                lightTextView), R.string.light);

        // 地磁気センサーを取得して登録する
        getSensorAndAddToListener(Sensor.TYPE_MAGNETIC_FIELD,
                new MagneticFieldSensorListener(magneticFieldTextView),
                R.string.magneticField);

        // 圧力センサーを取得して登録する
        getSensorAndAddToListener(Sensor.TYPE_PRESSURE,
                new PressureSensorListener(pressureTextView), R.string.pressure);

        // 近接センサーを取得して登録する
        getSensorAndAddToListener(Sensor.TYPE_PROXIMITY,
                new ProximitySensorListener(proximityTextView),
                R.string.proximity);

        // 温度センサーを取得して登録する
        getSensorAndAddToListener(Sensor.TYPE_TEMPERATURE,
                new TemperatureSensorListener(temperatureTextView),
                R.string.temperature);

        // Android2.2での未サポートセンサー
        gravityTextView.setText(String.format(
                getString(R.string.not_support_format) + "\n",
                getString(R.string.gravity)));                         // (8)
        linearAccelerationTextView.setText(String.format(
                getString(R.string.not_support_format) + "\n",
                getString(R.string.linearAcceleration)));
        rotationVectorTextView.setText(String.format(
                getString(R.string.not_support_format) + "\n",
                getString(R.string.rotationVector)));
    }

    /**
     * センサーを取得して、リスナーを設定する。
     * @param type センサーのタイプ
     * @param listener 設定するべきリスナー
     * @param name センサー名のID
     */
    private void getSensorAndAddToListener(int type,
            SensorEventListenerAdapter listener, int name) {           // (9)
        List<Sensor> sensors = sensorManager.getSensorList(type);      // (10)
        if (sensors.size() > 0) {
            sensorEventListenerList.add(listener);                     // (11)
            for (Sensor sensor : sensors) {
                sensorManager.registerListener(listener, sensor,
                        SensorManager.SENSOR_DELAY_NORMAL);            // (12)
            }

        } else {
            listener.getTextView().setText(
                    String.format(getString(R.string.no_sensor_format) + "\n",
                            getString(name)));                        // (13)
        }
    }

    @Override
    public void onPause() {
        super.onPause();

        // すべてのリスナーを解除する
        for (SensorEventListener listener : sensorEventListenerList) {
            sensorManager.unregisterListener(listener);               // (14)
        }
    }

}
---

(1)(4) 各種センサーから取得した値を表示するためのテキストビューを宣言する。

    private TextView acceleraterTextView; // 加速度センサー          // (1)

    acceleraterTextView = (TextView) findViewById(R.id.accelerater); // (4)


(2)(5) SensorManager を宣言する。

    private SensorManager sensorManager;                             // (2)
    sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);// (5)


(3)(6)(7)(14) onPause()が呼ばれたときに、すべてのリスナーを解除するために、センサーのリスナーを登録しておくリストを宣言する。

    private List<SensorEventListener> sensorEventListenerList; 
        // onPause()が呼ばれたときに、すべてのリスナーを解除するため // (3)
    sensorEventListenerList = new ArrayList<SensorEventListener>();  // (6)


リスナーの解除は、(14) にあるように、SensorManager クラスの unregisterListener(
listener) メソッドを使う。

    sensorManager.unregisterListener(listener);               // (14)


(7)、(9)~(13) センサーのデバイスを取得して、そのリスナーと紐付ける

    getSensorAndAddToListener(Sensor.TYPE_ACCELEROMETER,
                new AcceleraterSensorListener(acceleraterTextView),
                R.string.accelerater);                               // (7)

これは、自分で定義したプライベート・メソッドを呼び出す。
メソッドの詳細は、(9)~(13) を参照のこと。


    private void getSensorAndAddToListener(int type,
            SensorEventListenerAdapter listener, int name) {           // (9)

メソッドの宣言。センサーのタイプ type を受け取り、そのタイプのリスナーをリスト 
listener に入れて戻す。そのとき、そのセンサーが実機にない場合は、センサー名の ID
 name を基に、メッセージを出す。


    List<Sensor> sensors = sensorManager.getSensorList(type);      // (10)

センサーのタイプ type を基に、センサーを取得する。引数の type は、センサーの種類
を表す定数で、Sensor クラスに定数が定義されている。


    sensorEventListenerList.add(listener);                     // (11)

(10) で取得したセンサーをリストに登録する。このとき、センサーがなければ、リスト
には登録しないので、まずはリスナーを登録する。

    sensorManager.registerListener(listener, sensor,
             SensorManager.SENSOR_DELAY_NORMAL);            // (12)

センサーをリスナーに登録する。

    getString(name)));                        // (13)

センサーがない場合に、センサー名を取得する。name はりソース ID。


(8) センサーがサポートされていないとき、その旨のメッセージを表示する。

    gravityTextView.setText(String.format(
            getString(R.string.not_support_format) + "\n",
            getString(R.string.gravity)));                         // (8)


◆ センサーのリスナーに対するアダプターのクラス。

SensorEventListener インターフェースを実装する。これは、
SensorEventListenerAdapter クラスとするが、実際にはセンサーから取得する値は、1次
元のパターンと3次元のパターンがある。そのため、共通のメンバーを持つスーパークラ
ス SensorEventListenerAdapter をつくり、それぞれの次元の値を表示する用のサブクラ
スとして、OneDimensionalSensorListenerAdapter クラスと 
ThreeDimensionalSensorListenerAdapter クラスをつくる。実際のセンサーに対するリス
ナー・クラスは、このいずれかのクラスを継承する。

SensorEventListenerAdapter クラスは、インスタンス変数として、センサーから取得し
た値を表示するテキスト・ビューを持つ。
また、実装すべきメソッドのうち、onAccuracyChanged() を実装するが、中は何もしてい
ない。

□ SensorEventListenerAdapter.java
---
package jp.marunomaruno.android.sample;

import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.widget.TextView;

/**
 * リスナー用アダプター
 * @author maruno
 * @version 1.0, 2011-06-23
 * @since 1.0
 */
public abstract class SensorEventListenerAdapter implements SensorEventListener {    // (1)

    protected TextView textView;    // (2)

    public SensorEventListenerAdapter(TextView textView) {
        this.textView = textView;
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {    // (3)
        assert true; // 何もしない    // (4)
    }

    public TextView getTextView() {
        return textView;
    }
}
---

(1) センサー・リスナー・インターフェースのアダプター・クラス

SensorEventListener インターフェースを実装する。

    public abstract class SensorEventListenerAdapter implements SensorEventListener {    // (1)


(2) 取得した値を表示するためのテキストビュー・オブジェクト

    protected TextView textView;    // (2)


(3)、(4) センサーの精度が変わったときのハンドラー・メソッドの実装

メソッドとしては、何もしない。ここでは、何もしないことを明記するため、
    assert true;
を使っている。

    public void onAccuracyChanged(Sensor sensor, int accuracy) {    // (3)
    assert true; // 何もしない    // (4)


□ OneDimensionalSensorListenerAdapter.java
---
package jp.marunomaruno.android.sample;

import android.hardware.SensorEvent;
import android.widget.TextView;

/**
 * 1次元の値のリスナー用アダプター
 * @author maruno
 * @version 1.0, 2011-06-23
 * @since 1.0
 */
public abstract class OneDimensionalSensorListenerAdapter extends
        SensorEventListenerAdapter {    // (1)

    public OneDimensionalSensorListenerAdapter(TextView textView) {
        super(textView);
    }

    /**
     * センサーの値をTextViewに表示する。
     * @param event センサー・イベント
     * @param name センサー名のリソースID
     * @param unit 単位名のリソースID
     */
    public void out(SensorEvent event, int name, int unit) {
        // 値を取得する
        float x = event.values[0];    // (2)

        // TextViewに表示する
        textView.setText(String.format(
                textView.getResources().getString(R.string.sensor_1d_value_format) + "\n", 
                textView.getResources().getString(name), 
                event.sensor.getName(), 
                textView.getResources().getString(unit),
                event.sensor.getPower(),
                x
                ));
    }
}
---

(1) 1次元用のアダプター・クラス

public abstract class OneDimensionalSensorListenerAdapter extends
        SensorEventListenerAdapter {    // (1)

(2) センサーからの値を取得

    float x = event.values[0];    // (2)

今回は、1次元の値なので、values[0] のみを取得。


□ ThreeDimensionalSensorListenerAdapter.java
---
package jp.marunomaruno.android.sample;

import android.hardware.SensorEvent;
import android.widget.TextView;

/**
 * 3次元の値のリスナー用アダプター
 * @author maruno
 * @version 1.0, 2011-06-23
 * @since 1.0
 */
public abstract class ThreeDimensionalSensorListenerAdapter extends
        SensorEventListenerAdapter {    // (1)

    public ThreeDimensionalSensorListenerAdapter(TextView textView) {
        super(textView);
    }

    /**
     * センサーの値をTextViewに表示する。
     * @param event センサー・イベント
     * @param name センサー名のリソースID
     * @param unit 単位名のリソースID
     */
    public void out(SensorEvent event, int name, int unit) {
        // 値を取得する
        float x = event.values[0];    // (2)
        float y = event.values[1];    // (3)
        float z = event.values[2];    // (4)

        // TextViewに表示する
        textView.setText(String.format(
                textView.getResources().getString(R.string.sensor_3d_value_format) + "\n", 
                textView.getResources().getString(name), 
                event.sensor.getName(), 
                textView.getResources().getString(unit),
                event.sensor.getPower(),
                x, y, z
            ));
    }
}
---

(1) 3 次元用のアダプター・クラス

public abstract class ThreeDimensionalSensorListenerAdapter extends
        SensorEventListenerAdapter {    // (1)

(2)~(4) センサーからの値を取得

    float x = event.values[0];    // (2)
    float y = event.values[1];    // (3)
    float z = event.values[2];    // (4)

今回は、3 次元の値なので、values[0]~values[2] を取得。


◆ センサーのリスナーのクラス。

それぞれのセンサーに対するリスナーのクラス。
取得する値が 1 次元か、3 次元かによって、OneDimensionalSensorListenerAdapter ク
ラスか ThreeDimensionalSensorListenerAdapter クラスを継承する。


□ AcceleraterSensorListener.java
---
package jp.marunomaruno.android.sample;

import android.hardware.SensorEvent;
import android.widget.TextView;

/**
 * 加速度センサーのリスナー
 * @author maruno
 * @version 1.0, 2011-06-23
 * @since 1.0
 */
public class AcceleraterSensorListener extends
        ThreeDimensionalSensorListenerAdapter {    // (1)

    public AcceleraterSensorListener(TextView textView) {
        super(textView);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {    // (2)
        out(event, R.string.accelerater, R.string.acceleraterUnit);
    }
}
---

(1) 加速度センサーのハンドラー・クラス

    public class AcceleraterSensorListener extends
        ThreeDimensionalSensorListenerAdapter {    // (1)

3次元値なので、ThreeDimensionalSensorListenerAdapter クラスを継承する。


(2) センサーの値が変化したときに呼ばれるハンドラー・メソッド

    public void onSensorChanged(SensorEvent event) {    // (2)


□ GyroscopeSensorListener.java

3次元の値なので、AcceleraterSensorListener.java と同じパターン


□ LightSensorListener.java
---
package jp.marunomaruno.android.sample;

import android.hardware.SensorEvent;
import android.widget.TextView;

/**
 * 光センサーのリスナー
 * @author maruno
 * @version 1.0, 2011-06-23
 * @since 1.0
 */
public class LightSensorListener extends OneDimensionalSensorListenerAdapter {

    public LightSensorListener(TextView textView) {
        super(textView);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        out(event, R.string.light, R.string.lightUnit);
    }
}
---

□ MagneticFieldSensorListener.java
3 次元の値なので、AcceleraterSensorListener.java と同じパターン


□ PressureSensorListener.java

1 次元の値なので、LightSensorListener.java と同じパターン


□ ProximitySensorListener.java

1 次元の値なので、LightSensorListener.java と同じパターン


□ TemperatureSensorListener.java

1 次元の値なので、LightSensorListener.java と同じパターン


■ レイアウト

□ 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:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/accelerater"
    android:id="@+id/accelerater"
    />
    
    
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/gravity"
    android:id="@+id/gravity"
    />
    
    
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/gyroscope"
    android:id="@+id/gyroscope"
    />
    
    
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/light"
    android:id="@+id/light"
    />
    
    
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/magneticField"
    android:id="@+id/magneticField"
    />
    
    
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/linearAcceleration"
    android:id="@+id/linearAcceleration"
    />

    
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/pressure"
    android:id="@+id/pressure"
    />
    
    
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/proximity"
    android:id="@+id/proximity"
    />
    

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/rotationVector"
    android:id="@+id/rotationVector"
    />
    
    
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/temperature"
    android:id="@+id/temperature"
    />
    
</LinearLayout>
---


■ 文字列の定数

□ プログラム中での取得

ここに文字列の名前と値を
    <string name="名前">値</string>
というように定義しておくと、プログラムの中では、
    R.string.名前
でその文字列のIDを取得できる。
そのIDをつかって、ActivityクラスのgetString()メソッドを使えば、文字列の値を取得
できる。したがって、Activityクラスのサブクラスでは、
    getString(R.string.名前)
とすれば、名前に対する文字列の値を取得できる。

また、Activityクラスのサブクラスでない場合、たとえば、リスナークラスで使いたいと
きは、Resoucesオブジェクトを取得して、その中のgetString()メソッドを使う。
Resoucesオブジェクトは、resディレクトリーをカプセル化したオブジェクトである。
Resoucesオブジェクトの取得は、たとえば、Viewクラスであれば、
    getResources()
メソッドがある。したがって、
    textView.getResources().getString(R.string.名前)
で、名前に対する文字列の値を取得できる。


□ res/values/string.xml

センサーの名前と合わせて、そのセンサーで取得する値の単位も定義した。

---

<resources>
    <string name="app_name">SensorValue01</string>
    <string name="accelerater">加速度</string>
    <string name="acceleraterUnit">m/s^2</string>
    <string name="gravity">重力</string>
    <string name="gravityUnit">m/s^2</string>
    <string name="gyroscope">ジャイロ</string>
    <string name="gyroscopeUnit">rad/s</string>
    <string name="light">光</string>
    <string name="lightUnit">lux</string>
    <string name="magneticField">地磁気</string>
    <string name="magneticFieldUnit">uT</string>
    <string name="linearAcceleration">線形加速度</string>
    <string name="linearAccelerationUnit">m/s^2</string>
    <string name="pressure">圧力</string>
    <string name="pressureUnit">Pa</string> 
    <string name="proximity">近接</string>
    <string name="proximityUnit">cm</string>
    <string name="rotationVector">回転ベクトル</string>
    <string name="rotationVectorUnit">-</string> 
    <string name="temperature">温度</string>
    <string name="temperatureUnit">℃</string> 
    <string name="sensor_1d_value_format">
                %1$s: %2$s = %5$.2f (%3$s), 消費バッテリー = %4$.6f mA</string>
    <string name="sensor_3d_value_format">
    %1$s: %2$s = (X, Y, Z) = (%5$.2f, %6$.2f, %7$.2f) (%3$s), 消費バッテリー = %4$.6f mA
    </string>
    <string name="not_support_format">
                Android2.2 では、%s センサーはサポートされていません。</string>
    <string name="no_sensor_format">%s センサーはありません。</string>
</resources>
---




最新の画像もっと見る

コメントを投稿