android使用磁场传感器和加速度传感器确定当前朝向(即:方位角),以及常见问题的解决办法

原文链接:http://blog.csdn.net/warren288/article/details/43274647

1,功能实现

确定当前设备朝向的方式有两种,一种是是直接使用 方向传感器 Sensor.TYPE_ORIENTATION,另一种是结合使用Sensor.TYPE_ACCELEROMETER和Sensor.TYPE_MAGNETIC_FIELD;前一种已经不建议使用了,因为它无法根据应用的横竖屏与设备的自然方向调整结果方向,且精确度不如后一种方式。

后一种方式的实现如下,比较关键的地方都写注释了。

[java]  view plain  copy
  1. <pre name="code" class="java">    /** 
  2.      * 方向传感器中心。<br> 
  3.      * 使用方法为:创建对象后使用 {@link #init(Activity)} 初始化上下文环境,使用 
  4.      * {@link #registerOrientationListener(IListener)} 注册 
  5.      * 方向变化监听器,使用完毕后(一般在 {@link #init(Activity)}中所使用 {@link Activity} 
  6.      * 销毁时), {@link #unregister()}取消本类内部注册的 {@link SensorEventListener} 
  7.      * @author yangsheng 
  8.      * @date 2015年1月28日 
  9.      */  
  10.     private static class SensorCenter {  
  11.   
  12.         private float[] accelerValues = new float[3];  
  13.         private float[] magneticValues = new float[3];  
  14.   
  15.         private final SensorEventListener sensorListener = new SensorEventListener() {  
  16.   
  17.             @Override  
  18.             public void onSensorChanged(SensorEvent event) {  
  19.   
  20.                 switch (event.sensor.getType()) {  
  21.   
  22.                 // 深度复制,可避免部分设备下 SensorManager.getRotationMatrix 返回false的问题  
  23.                 case Sensor.TYPE_ACCELEROMETER:  
  24.                     for (int i = 0; i < 3; i++) {  
  25.                         accelerValues[i] = event.values[i];  
  26.                     }  
  27.   
  28.                     if (listener != null) {  
  29.                         listener.onCall(getAngle());  
  30.                     }  
  31.                     break;  
  32.                 case Sensor.TYPE_MAGNETIC_FIELD:  
  33.                     for (int i = 0; i < 3; i++) {  
  34.                         magneticValues[i] = event.values[i];  
  35.                     }  
  36.                     break;  
  37.                 case Sensor.TYPE_GYROSCOPE:  
  38.                     break;  
  39.                 default:  
  40.                     break;  
  41.                 }  
  42.             }  
  43.   
  44.             @Override  
  45.             public void onAccuracyChanged(Sensor sensor, int accuracy) {  
  46.             }  
  47.         };  
  48.   
  49.         private SensorManager sensorManager;  
  50.         private Activity activity;  
  51.   
  52.         private IListener<Float> listener;  
  53.   
  54.         private int axis_x;  
  55.         private int axis_y;  
  56.   
  57.         public SensorCenter init(Activity activity) {  
  58.   
  59.             this.activity = activity;  
  60.             this.sensorManager = (SensorManager) this.activity  
  61.                         .getSystemService(Context.SENSOR_SERVICE);  
  62.   
  63.             // 注册三个传感器监听,加速度传感器、磁场传感器、陀螺仪传感器。  
  64.             // 陀螺仪传感器是可选的,也没有实际用处,但是在部分设备上,如果没注册它,会导致  
  65.             // SensorManager.getRotationMatrix 返回false  
  66.             sensorManager.registerListener(sensorListener,  
  67.                         sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),  
  68.                         SensorManager.SENSOR_DELAY_UI);  
  69.             sensorManager.registerListener(sensorListener,  
  70.                         sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),  
  71.                         SensorManager.SENSOR_DELAY_UI);  
  72.             sensorManager.registerListener(sensorListener,  
  73.                         sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE),  
  74.                         SensorManager.SENSOR_DELAY_UI);  
  75.   
  76.             initIn();  
  77.             return this;  
  78.         }  
  79.   
  80.         private void initIn() {  
  81.               
  82.             // 根据当前上下文的屏幕方向调整与自然方向的相对关系。  
  83.             // 当设备的自然方向是竖直方向(比如,理论上说所有手机的自然方向都是都是是竖直方向),而应用是横屏时,  
  84.             // 需要将相对设备自然方向的方位角转换为相对水平方向的方位角。  
  85.   
  86.             WindowManager wm = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);  
  87.             Display dis = wm.getDefaultDisplay();  
  88.             int rotation = dis.getRotation();  
  89.   
  90.             axis_x = SensorManager.AXIS_X;  
  91.             axis_y = SensorManager.AXIS_Y;  
  92.             switch (rotation) {  
  93.             case Surface.ROTATION_0:  
  94.                 break;  
  95.             case Surface.ROTATION_90:  
  96.                 axis_x = SensorManager.AXIS_Y;  
  97.                 axis_y = SensorManager.AXIS_MINUS_X;  
  98.                 break;  
  99.             case Surface.ROTATION_180:  
  100.                 axis_x = SensorManager.AXIS_X;  
  101.                 axis_y = SensorManager.AXIS_MINUS_Y;  
  102.                 break;  
  103.             case Surface.ROTATION_270:  
  104.                 axis_x = SensorManager.AXIS_MINUS_Y;  
  105.                 axis_y = SensorManager.AXIS_X;  
  106.                 break;  
  107.   
  108.             default:  
  109.                 break;  
  110.             }  
  111.         }  
  112.   
  113.         /** 
  114.          * 注册方向变换监听 
  115.          * @param listener, {@link IListener#onCall(Object)} 
  116.          *            中的参数为相对正北方向的方位角,范围:-180~180 
  117.          */  
  118.         public void registerOrientationListener(IListener<Float> listener) {  
  119.             this.listener = listener;  
  120.         }  
  121.   
  122.         /** 
  123.          * 取方位角 
  124.          * @return 
  125.          */  
  126.         private float getAngle() {  
  127.   
  128.             float[] inR = new float[9];  
  129.             // 第一个是方向角度参数,第二个参数是倾斜角度参数  
  130.             SensorManager.getRotationMatrix(inR, null, accelerValues, magneticValues);  
  131.   
  132.             float[] outR = new float[9];  
  133.             float[] orientationValues = new float[3];  
  134.   
  135.             // 根据当前上下文的屏幕方向调整与自然方向的相对关系。  
  136.             // 当设备的自然方向是竖直方向(比如,理论上说所有手机的自然方向都是都是是竖直方向,而有些平板的自然方向是水平方向),而应用是横屏时,  
  137.             // 需要将相对设备自然方向的方位角转换为相对水平方向的方位角。  
  138.             SensorManager.remapCoordinateSystem(inR, axis_x, axis_y, outR);  
  139.             SensorManager.getOrientation(outR, orientationValues);  
[java]  view plain  copy
  1. <span style="white-space:pre">            </span>// <span style="font-family: Arial, Helvetica, sans-serif;">orientationValues里的值都是弧度值,需要转换为角度值</span>  
  2.             return (float) Math.toDegrees(orientationValues[0]);  
  3.         }  
  4.   
  5.         /** 
  6.          * 取消注册的传感器监听 
  7.          */  
  8.         public void unregister() {  
  9.             try {  
  10.                 sensorManager.unregisterListener(sensorListener);  
  11.             } catch (Exception e) {  
  12.                 LogTool.exception(e);  
  13.             }  
  14.         }  
  15.     }  

 
  

2,常见问题

最常见的问题是  SensorManager.getRotationMatrix 返回false,传进去的第一个参数没有被正确赋值,于是最终获得方位角就是0。
这个问题出现的原因一般有以下几种:
(1)在注册的传感器监听器的  onSensorChanged 中没有使用深复制,而是直接使用浅复制  accelerValues = event.values; 这种情况下,部分设备可以正确地取到最终的方位角,但是部分设备下就会出现  SensorManager.getRotationMatrix  失败的问题。应该是不同设备的系统使用了不同的方式重写了Android的这一段代码导致的。
(2)在注册传感器监听器时,只注册了 Sensor.TYPE_ACCELEROMETER和Sensor.TYPE_MAGNETIC_FIELD两种监听器,没有注册  Sensor.TYPE_GYROSCOPE; 这种情况下,部分设备中可以正确地取到最终的方位角,但是部分设备下就会出现上述问题。

由于不同设备的自然方向可能不同,有些是竖直方向有些是水平方向,而应用可能是竖屏可能是横屏,所以在很多情况下,我们需要在取方位角之前先将自然坐标系投影到我们需要的实际坐标系上去,使用 SensorManager.remapCoordinateSystem 方法。


猜你喜欢

转载自blog.csdn.net/bin470398393/article/details/78919599