文章目錄
- 概述
- getOrientation 方法根據 旋轉矩陣R 獲取 設備旋轉弧度
- getRotationMatrix 方法根據 地磁場、加速度傳感器對象 獲取 旋轉矩陣R
- 代碼
- 參考資料
概述
Sensor.TYPE_ORIENTATION 常數在 API 8 中已棄用,官方推薦使用 SensorManager.getOrientation()
替代。關于 Orientation Sensor(被棄用的方向傳感器) 在官方文檔中的概述里有這樣一句話:
The orientation sensor is software-based and derives its data from the accelerometer and the geomagnetic field sensor. (方向傳感器是基于軟件的,并且它的數據是通過加速度傳感器和磁場傳感器共同獲得的)
上面的描述其實少了一個重要角色,即 SensorManager.getOrientation()
。方向傳感器 在被棄用之前,正是通過 SensorManager.getOrientation()
來借助 加速度傳感器(Sensor.TYPE_ACCELEROMETER) 和 地磁場傳感器(TYPE_MAGNETIC_FIELD) 的數據得到的。
getOrientation 方法根據 旋轉矩陣R 獲取 設備旋轉弧度
官方文檔中,對于 getOrientation
方法的介紹如下:
該函數有兩個參數,R
和 values
。傳入時 R
有具體值而 values
是空的,然后在方法內部根據 旋轉矩陣R 計算設備的方向,將結果存儲在 values
中:
- values[0] 記錄著手機圍繞 Z 軸的旋轉弧度
- values[1] 記錄著手機圍繞 X 軸的旋轉弧度
- values[2] 記錄著手機圍繞 Y 軸的旋轉弧度
而后可以通過 Math.toDegrees()
方法將旋轉弧度轉化為角度。
但是這里還有個問題,旋轉矩陣R 的值從何而來呢?事實上,其值通過我們之前提到 加速度傳感器(Sensor.TYPE_ACCELEROMETER) 和 地磁場傳感器(TYPE_MAGNETIC_FIELD) 的獲得。
首先通過 SensorEvent
對象獲得兩個傳感器對象 Sensor
,一個是 加速度傳感器,另一個是 地磁場傳感器。
public void onSensorChanged(SensorEvent event) {// SensorEvent:保存精度(accuracy)、傳感器類型(sensor)、時間戳(timestamp)// 以及不同傳感器(Sensor)具有的不同傳感器數組(values)。SensorManager.getOrientation(r, values);// TYPE_MAGNETIC_FIELD:描述磁場傳感器類型的常量。if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {geomagnetic = event.values; // 地磁場傳感器對象}// TYPE_ACCELEROMETER:描述加速度傳感器類型的常量。if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {gravity = event.values; // 加速度傳感器對象}
}
getRotationMatrix 方法根據 地磁場、加速度傳感器對象 獲取 旋轉矩陣R
之后通過這兩個對象結合 SensorManager.getRotationMatrix()
方法獲取 旋轉矩陣R 的具體值。官方文檔中,對于 getRotationMatrix
方法的介紹如下:
該函數有四個參數,通過計算 gravity
和 geomagnetic
得到 旋轉矩陣R。(第二個參數 傾斜矩陣I 用于將磁場數據轉換進實際的重力坐標系中,一般默認設置為NULL即可。)
代碼
/** 方向傳感器 */
public class MyOrientationListener implements SensorEventListener {private static final String TAG = "WeatherActivity";private final Context context;private SensorManager sensorManager;private Sensor magneticSensor, accelerometerSensor;private float[] gravity = new float[3];private float[] geomagnetic= new float[3];private OnOrientationListener onOrientationListener; //內部接口實現回調private double lastX;/** 當有新的傳感器事件時(手機方向改變時調用)調用。 */@Overridepublic void onSensorChanged(SensorEvent event) {Log.e(TAG, "onSensorChanged 開始");// SensorEvent:保存精度(accuracy)、傳感器類型(sensor)、時間戳(timestamp)// 以及不同傳感器(Sensor)具有的不同傳感器數組(values)。// TYPE_MAGNETIC_FIELD:描述磁場傳感器類型的常量。if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {geomagnetic = event.values;Log.e(TAG, "onSensorChanged 得到磁場傳感器: " + Arrays.toString(geomagnetic));}// TYPE_ACCELEROMETER:描述加速度傳感器類型的常量。if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {gravity = event.values;Log.e(TAG, "onSensorChanged 得到加速度傳感器: " + Arrays.toString(gravity));}getValue();}/** 當注冊傳感器的精度發生變化時調用。 */@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {}/** 通過加速度和磁場變化獲取方向變化的信息 */public void getValue() {//初始化數組float[] values = new float[3]; // 用來保存手機的旋轉弧度float[] r = new float[9]; // 被填充的旋轉矩陣// 傳入gravity和geomagnetic,通過計算它們得到旋轉矩陣R。// 而第二個參數傾斜矩陣I是用于將磁場數據轉換進實際的重力坐標系中的,一般默認設置為NULL即可。SensorManager.getRotationMatrix(r, null, gravity, geomagnetic);// 根據旋轉矩陣R計算設備的方向,將結果存儲在values中。// values[0]記錄著手機圍繞 Z 軸的旋轉弧度,// values[1]記錄著手機圍繞 X 軸的旋轉弧度,// values[2]記錄著手機圍繞 Y 軸的旋轉弧度。SensorManager.getOrientation(r, values);Log.e(TAG, "getValue R: " + Arrays.toString(r));Log.e(TAG, "getValue values: " + Arrays.toString(values));// 旋轉弧度轉為角度float pitch = (float) Math.toDegrees(values[0]);Log.e(TAG, "getValue pitch: "+ pitch);if (Math.abs(lastX) > 1.0) { // 設置條件防止頻繁回調onOrientationListener.onOrientationChanged(pitch);}lastX = pitch;}public interface OnOrientationListener{void onOrientationChanged(float x);}public void setOnOrientationListener(OnOrientationListener onOrientationListener){this.onOrientationListener = onOrientationListener;Log.e(TAG, "setOnOrientationListener: 接口設置完成");}public MyOrientationListener(Context context){this.context=context;}public void onStart(){sensorManager = (SensorManager) context.getSystemService(SENSOR_SERVICE);if (sensorManager != null) { // 初始化兩個傳感器// getDefaultSensor:獲取Sensor,使用給定的類型和喚醒屬性返回傳感器。magneticSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);}if (magneticSensor != null) {assert sensorManager != null;sensorManager.registerListener(this, magneticSensor,SensorManager.SENSOR_DELAY_UI);}if (accelerometerSensor != null) {assert sensorManager != null;sensorManager.registerListener(this, accelerometerSensor,SensorManager.SENSOR_DELAY_UI);}}public void onStop(){sensorManager.unregisterListener(this); // 傳感器解除綁定}
}
參考資料
傳感器的相關知識講的很詳細
Android之傳感器(三)方向傳感器