Android 传感器篇:(一)方向传感器

从这篇文章开始,主要记录android 传感器相关的知识:

这是这个系列的第一篇,主要讲解android 主要的传感器,以及方向传感器详解。

一:传感器基础

  官网上我们看到,android中涉及的传感器主要有

   1:Motion sensors 

         这些传感器沿三个轴测量加速力和旋转力。此类别包括加速度计,重力传感器,陀螺仪和旋转矢量传感器。

   2:Position sensors

      这些传感器测量各种环境参数,例如环境空气温度和压力,照明和湿度。此类别包括气压计,光度计和温度计。

   3:Environment sensrs

     这些传感器测量设备的物理位置。此类别包括方向传感器和磁力计。

   下面这个截图是android平台支持的传感器类型。

   

  传感器部分主要用到几个类: 1:sensor 2:sensorManager 3:SensorEvent 4:SensorEventListener

  我们先通过sensorManger获取下android设备支持的sensor列表,同时熟悉下对应的方法。

   第一步:获取传感器管理对象。

 manager= (SensorManager) getSystemService(SENSOR_SERVICE);

  第二步:获取所有支持的sensor

 // 得到设备支持的所有传感器的List
   sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);

   第三步:编写adapter,展示。这里展示的sensor的name属性。

  

 这里我是在模拟器运行的。4.4的设备,所以支持的较少。

二:方向传感器   

    这一部分具体介绍传感器的方向传感器。

    具体对应的就是

 我们通过TYPE_ORIENTATION来获取方向传感器对象,具体获取方法如下:

   //获取传感器管理对象
  manager= (SensorManager) getSystemService(SENSOR_SERVICE);
  //获取方向传感器
  orientationSensor=manager.getDefaultSensor(Sensor.TYPE_ORIENTATION);

 获取到对象后,我们需要在onresume()方法中注册监听事件,在onpause()中解除监听。

  

  protected void onResume() {
        super.onResume();
        if (orientationSensor!=null){
            
//三个参数分别是SensorEventListener,sensor,速率
           manager.registerListener(this,orientationSensor,SensorManager.SENSOR_DELAY_NORMAL);
        }
 }


  @Override
    protected void onPause() {
        super.onPause();
        if (manager!=null){
            manager.unregisterListener(this);
        }
    }

  注册监听时第三个参数指的是延迟时间:有四种类型

 /** get sensor data as fast as possible */
    public static final int SENSOR_DELAY_FASTEST = 0;
    /** rate suitable for games */
    public static final int SENSOR_DELAY_GAME = 1;
    /** rate suitable for the user interface  */
    public static final int SENSOR_DELAY_UI = 2;
    /** rate (default) suitable for screen orientation changes */
    public static final int SENSOR_DELAY_NORMAL = 3;

 查看源码,我们可以得到每个类型对应的延迟时间。

private static int getDelay(int rate) {
        int delay = -1;
        switch (rate) {
            case SENSOR_DELAY_FASTEST:
                delay = 0;
                break;
            case SENSOR_DELAY_GAME:
                delay = 20000;
                break;
            case SENSOR_DELAY_UI:
                delay = 66667;
                break;
            case SENSOR_DELAY_NORMAL:
                delay = 200000;
                break;
            default:
                delay = rate;
                break;
        }
        return delay;
    }

  具体的数据在SensorEventListener的返回方法中,下面是对应的方法。

  根据SensorEvent.values获取返回的数据,我们可以看到返回的是一个数组。

//当有新的传感器事件时调用 
    @Override
    public void onSensorChanged(SensorEvent event) {
            float[] values = event.values;
            StringBuilder sb=new StringBuilder();
            sb.append("z轴:"+values[0]+"\n");
            sb.append("x轴:"+values[1]+"\n");
            sb.append("y轴:"+values[2]+"\n");
            binding.tvValue.setText("旧API:\n"+sb.toString());

    }
    //当已注册传感器的精度发生变化时调用
     @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

 运行下看下结果:

 当我们使用TYPE_ORIENTATION时,会发现这是个已经过时的方法,那最新的获取设备方向的方法是什么呢?

 /**
     * A constant describing an orientation sensor type.
     * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values}
     * for more details.
     *
     * @deprecated use {@link android.hardware.SensorManager#getOrientation
     *             SensorManager.getOrientation()} instead.
     */
    @Deprecated
    public static final int TYPE_ORIENTATION = 3;

 查看源码我们发现,官方已经给出了替代的方法,那就是使用SensorManager.getOrientation()。

 那这个方法具体是怎么使用,或者返回的是什么呢?

/* @param R
     *        rotation matrix see {@link #getRotationMatrix}.
     *
     * @param values
     *        an array of 3 floats to hold the result.
     * 
 */

public static float[] getOrientation(float[] R, float[] values) {
        /*
         * 4x4 (length=16) case:
         *   /  R[ 0]   R[ 1]   R[ 2]   0  \
         *   |  R[ 4]   R[ 5]   R[ 6]   0  |
         *   |  R[ 8]   R[ 9]   R[10]   0  |
         *   \      0       0       0   1  /
         *
         * 3x3 (length=9) case:
         *   /  R[ 0]   R[ 1]   R[ 2]  \
         *   |  R[ 3]   R[ 4]   R[ 5]  |
         *   \  R[ 6]   R[ 7]   R[ 8]  /
         *
         */
        if (R.length == 9) {
            values[0] = (float) Math.atan2(R[1], R[4]);
            values[1] = (float) Math.asin(-R[7]);
            values[2] = (float) Math.atan2(-R[6], R[8]);
        } else {
            values[0] = (float) Math.atan2(R[1], R[5]);
            values[1] = (float) Math.asin(-R[9]);
            values[2] = (float) Math.atan2(-R[8], R[10]);
        }

        return values;
    }

 从源码这里我们可以看到这个方法有两个参数,

  1. 第一个参数是R用来保存磁场和加速度的数据,通过该函数获取方位角。
  2. 第二个参数是函数输出,数据自动填充。

也就是说第二个参数就是我们需要的模拟方向的数据。

  • value[0] 方位角(绕-z轴旋转的度数)。这是设备当前罗盘方向与磁北极之间的角度。如果器件的上边缘朝向磁北,则方位角为0度; 如果顶部边缘朝南,则方位角为180度。类似地,如果顶部边缘朝向东方,则方位角为90度,如果顶部边缘朝向西方,则方位角为270度。
  • value[1] 间距(围绕x轴的旋转度)。这是平行于设备屏幕的平面与平行于地面的平面之间的角度。如果您将设备平行于地面,底边与您最接近,并将设备的顶边向地面倾斜,则俯仰角变为正。向相反方向倾斜 - 将设备的顶部边缘移离地面 - 导致俯仰角变为负。值的范围是-180度到180度。
  • value[2] 滚动(绕y轴旋转的角度)。这是垂直于设备屏幕的平面与垂直于地面的平面之间的角度。如果您将设备平行于地面,底边最靠近您,并将设备的左边缘向地面倾斜,则滚动角度变为正值。向相反方向倾斜 - 将设备的右边缘朝向地面移动 - 导致滚动角度变为负值。值范围为-90度至90度。

 另外第一个参数数组R我们可以看到是通过getRotationMatrix()获取到的。

 public static boolean getRotationMatrix(float[] R, float[] I,
            float[] gravity, float[] geomagnetic) {
        // TODO: move this to native code for efficiency
        float Ax = gravity[0];
        float Ay = gravity[1];
        float Az = gravity[2];

        final float normsqA = (Ax * Ax + Ay * Ay + Az * Az);
        final float g = 9.81f;
        final float freeFallGravitySquared = 0.01f * g * g;
        if (normsqA < freeFallGravitySquared) {
            // gravity less than 10% of normal value
            return false;
        }

        final float Ex = geomagnetic[0];
        final float Ey = geomagnetic[1];
        final float Ez = geomagnetic[2];
        float Hx = Ey * Az - Ez * Ay;
        float Hy = Ez * Ax - Ex * Az;
        float Hz = Ex * Ay - Ey * Ax;
        final float normH = (float) Math.sqrt(Hx * Hx + Hy * Hy + Hz * Hz);

        if (normH < 0.1f) {
            // device is close to free fall (or in space?), or close to
            // magnetic north pole. Typical values are  > 100.
            return false;
        }
        final float invH = 1.0f / normH;
        Hx *= invH;
        Hy *= invH;
        Hz *= invH;
        final float invA = 1.0f / (float) Math.sqrt(Ax * Ax + Ay * Ay + Az * Az);
        Ax *= invA;
        Ay *= invA;
        Az *= invA;
        final float Mx = Ay * Hz - Az * Hy;
        final float My = Az * Hx - Ax * Hz;
        final float Mz = Ax * Hy - Ay * Hx;
        if (R != null) {
            if (R.length == 9) {
                R[0] = Hx;     R[1] = Hy;     R[2] = Hz;
                R[3] = Mx;     R[4] = My;     R[5] = Mz;
                R[6] = Ax;     R[7] = Ay;     R[8] = Az;
            } else if (R.length == 16) {
                R[0]  = Hx;    R[1]  = Hy;    R[2]  = Hz;   R[3]  = 0;
                R[4]  = Mx;    R[5]  = My;    R[6]  = Mz;   R[7]  = 0;
                R[8]  = Ax;    R[9]  = Ay;    R[10] = Az;   R[11] = 0;
                R[12] = 0;     R[13] = 0;     R[14] = 0;    R[15] = 1;
            }
        }
        if (I != null) {
            // compute the inclination matrix by projecting the geomagnetic
            // vector onto the Z (gravity) and X (horizontal component
            // of geomagnetic vector) axes.
            final float invE = 1.0f / (float) Math.sqrt(Ex * Ex + Ey * Ey + Ez * Ez);
            final float c = (Ex * Mx + Ey * My + Ez * Mz) * invE;
            final float s = (Ex * Ax + Ey * Ay + Ez * Az) * invE;
            if (I.length == 9) {
                I[0] = 1;     I[1] = 0;     I[2] = 0;
                I[3] = 0;     I[4] = c;     I[5] = s;
                I[6] = 0;     I[7] = -s;     I[8] = c;
            } else if (I.length == 16) {
                I[0] = 1;     I[1] = 0;     I[2] = 0;
                I[4] = 0;     I[5] = c;     I[6] = s;
                I[8] = 0;     I[9] = -s;     I[10] = c;
                I[3] = I[7] = I[11] = I[12] = I[13] = I[14] = 0;
                I[15] = 1;
            }
        }
        return true;
    }

我们看到getRotationMatrix()这个方法有四个参数,这四个参数分别是什么呢?

1:float[] R   是一个包含旋转矩阵的浮点数组,也是要填充的数组

2:float[] I     将磁场数据转换进实际的重力坐标中,一般传null。

3:float[] gravity 加速度传感器数据 数组长度3

4:float[] geomagnetic  地磁传感器数据 长度 3

具体使用步骤

首先定义参数:

  //加速度传感器数据
    private final float[] accelerometerReading = new float[3];
    //地磁传感器数据
    private final float[] magnetometerReading = new float[3];
    //旋转矩阵,用来保存磁场和加速度的数据
    private final float[] rotationMatrix = new float[9];
    //方向数据
    private final float[] orientationAngles = new float[3];

注册listener:

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

        //加速度传感器
       Sensor accelerometer = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        if (accelerometer != null) {
            manager.registerListener(this, accelerometer,
                    SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);
        }
        //地磁传感器
        Sensor magneticField = manager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        if (magneticField != null) {
            manager.registerListener(this, magneticField,
                    SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);
        }
    }

//另外这几行代码是在官网上复制的,但是有个问题,官网两个传感器在获取对象时传的type是同一个,注意下。。。。。

  

   @Override
    public void onSensorChanged(SensorEvent event) {
         //判断sensor类型

        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            System.arraycopy(event.values, 0, accelerometerReading,
                    0, accelerometerReading.length);
        } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
            System.arraycopy(event.values, 0, magnetometerReading,
                    0, magnetometerReading.length);
        }
        updateOrientationAngles();
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

    public void updateOrientationAngles() {
        // 更新旋转矩阵.
        // 参数1:
        // 参数2 :将磁场数据转换进实际的重力坐标中,一般默认情况下可以设置为null
        //  参数3:加速度
        // 参数4:地磁
        SensorManager.getRotationMatrix(rotationMatrix, null,
                accelerometerReading, magnetometerReading);

        //根据旋转矩阵计算设备的方向
        //参数1:旋转数组
        //参数2:模拟方向传感器的数据
        SensorManager.getOrientation(rotationMatrix, orientationAngles);
        StringBuilder sb=new StringBuilder();
        if (orientationAngles.length>=3){
            sb.append("z轴:"+orientationAngles[0]+"\n");
            sb.append("x轴:"+orientationAngles[1]+"\n");
            sb.append("y轴:"+orientationAngles[2]+"\n");
            binding.tvValueNew.setText("\n新API:\n"+sb.toString());
        }
    }

看下结果:

另外加个指南针图片,根据获取的degrees旋转。

 //顺时针转动为正,故手机顺时针转动时,图片得逆时针转动
            //让图片相对自身中心点转动,开始角度默认为0;此后开始角度等于上一次结束角度
            RotateAnimation ra = new RotateAnimation(fromDegrees, -degrees, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            //动画时间200毫秒
            ra.setDuration(200);
            ra.setFillAfter(true);
            binding.ivCompass.startAnimation(ra);
            fromDegrees = -degrees;

最后附上git的地址:

Github地址

猜你喜欢

转载自blog.csdn.net/qq_23025319/article/details/89920881