前回の記事で紹介した傾きと方位角を求める方法は、最新の Android OS では非推奨(deprecated)とされています。現在のリファレンスでは、加速度センサと方位角センサそれぞれの測定値から計算するよう求められています。開発者が記述すべきコードが増えるため、一見すると面倒になったように思えますが、この過程で得られた回転行列は、OpenGL などの 3D グラフィックス描画においてそのまま利用できるメリットもあって、総合的にはより汎用性を増したといえます。
変更点は 2 つ。リスナを登録する際に、傾き(TYPE_ORIENTATION)センサ一つで済んだものが、加速度(TYPE_ACCELEROMETER)センサと、磁気(TYPE_MAGNETIC_FIELD)センサを 2 つ必要とする点。そして、傾きと方位角を求める際に、両者のセンサの測定値から回転行列を求めて、最終的な解を得るという点です。
TYPE_ORIENTATION を指定した場合でも、加速度センサと磁気センサを用いて内部的に同じように計算していたと思うのですが、それを開発者が明示的に書く必要があるということです。
SensorManager
を取得するSensorManager
に、 加速度センサ(TYPE_ACCELEROMETER) と 磁気センサ(TYPE_MAGNETIC_FIELD) について、SensorEventListener
を登録するSensorEventListener
の onSensorChanged
メンバ関数がコールバックされるSensorManager
から SensorEventListener
を外すpackage com.example.helloworld; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.content.Context; import android.hardware.SensorManager; import android.hardware.SensorEventListener; import android.hardware.SensorEvent; import android.hardware.Sensor; import android.widget.TextView; public class MainActivity extends ActionBarActivity { private SensorManager mSensorManager = null; private SensorEventListener mSensorEventListener = null; private float[] fAccell = null; private float[] fMagnetic = null; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSensorManager = (SensorManager) getSystemService( Context.SENSOR_SERVICE ); mSensorEventListener = new SensorEventListener() { public void onSensorChanged (SensorEvent event) { // センサの取得値をそれぞれ保存しておく switch( event.sensor.getType()) { case Sensor.TYPE_ACCELEROMETER: fAccell = event.values.clone(); break; case Sensor.TYPE_MAGNETIC_FIELD: fMagnetic = event.values.clone(); break; } // fAccell と fMagnetic から傾きと方位角を計算する if( fAccell != null && fMagnetic != null ) { // 回転行列を得る float[] inR = new float[9]; SensorManager.getRotationMatrix( inR, null, fAccell, fMagnetic ); // ワールド座標とデバイス座標のマッピングを変換する float[] outR = new float[9]; SensorManager.remapCoordinateSystem( inR, SensorManager.AXIS_X, SensorManager.AXIS_Y, outR ); // 姿勢を得る float[] fAttitude = new float[3]; SensorManager.getOrientation( outR, fAttitude ); String buf = "---------- Orientation --------\n" + String.format( "方位角\n\t%f\n", rad2deg( fAttitude[0] )) + String.format( "前後の傾斜\n\t%f\n", rad2deg( fAttitude[1] )) + String.format( "左右の傾斜\n\t%f\n", rad2deg( fAttitude[2] )); TextView t = (TextView) findViewById( R.id.textView1 ); t.setText( buf ); } } public void onAccuracyChanged (Sensor sensor, int accuracy) {} }; } private float rad2deg( float rad ) { return rad * (float) 180.0 / (float) Math.PI; } protected void onStart() { // ⇔ onStop super.onStart(); mSensorManager.registerListener( mSensorEventListener, mSensorManager.getDefaultSensor( Sensor.TYPE_ACCELEROMETER ), SensorManager.SENSOR_DELAY_UI ); mSensorManager.registerListener( mSensorEventListener, mSensorManager.getDefaultSensor( Sensor.TYPE_MAGNETIC_FIELD ), SensorManager.SENSOR_DELAY_UI ); } protected void onStop() { // ⇔ onStart super.onStop(); mSensorManager.unregisterListener( mSensorEventListener ); } }
SensorManager
を取得します(概要の手順.1)SensorManager
に、加速度(TYPE_ACCELEROMETER)センサと、磁気(TYPE_MAGNETIC_FIELD)センサについて、SensorEventListener
を登録して、センサ値の取得を始めます(概要の手順.2)。回転行列の処理については、代数幾何のお話になるので私もよく判っていません*1。とりあえずオマジナイ的にコピー&ペーストで使えれば OK ということで。