Android 手机翻转实现技术

Android中检测重力感应变化大致需要下面几个步骤:

1) 得到传感器服务 getSystemService(SENSOR_SERVICE);

得到一个SensorManager,用来管理分配调度处理Sensor的工作,注意它并不服务运行于后台,真正属于Sensor的系统服务是SensorService,终端下#service list可以看到sensorservice: [android.gui.SensorServer]。

2) 得到传感器类型 getDefaultSensor(Sensor.TYPE_GRAVITY);

当然还有各种千奇百怪的传感器,可以查阅Android官网API或者源码Sensor.java。

3) 注册监听器 SensorEventListener

应用程序打开一个监听接口,专门处理传感器的数据,这个监听机制比较重要,被系统广泛使用。

4) 实现监听器的回调函数 onSensorChanged, onAccuracyChanged

很多移动设备都内置了感应器,android通过Sensor和SensorManager类抽象了这些感应器,通过这些类可以使用android设备的传感器

 

一 介绍Sensor类

扫描二维码关注公众号,回复: 11312959 查看本文章

 

SDK只有一句介绍“Class representing a sensor. Use getSensorList(int) to get the list of available Sensors.”,表示一个感应器的类,可以使用getSensorList方法(此方法属于接下来要讲的SensorManager)获得所有可用的感应器,该方法返回的是一个List<Sensor>

 

下面的列表显示了,Sensor所提供的所有服务

Constants

int TYPE_ACCELEROMETER A constant describing an accelerometer sensor type. //三轴加速度感应器 返回三个坐标轴的加速度 单位m/s2

int TYPE_ALL A constant describing all sensor types. //用于列出所有感应器

int TYPE_GRAVITY A constant describing a gravity sensor type. //重力感应器

int TYPE_GYROSCOPE A constant describing a gyroscope sensor type //陀螺仪 可判断方向 返回三个坐标轴上的角度

int TYPE_LIGHT A constant describing an light sensor type. //光线感应器 单位 lux 勒克斯

int TYPE_LINEAR_ACCELERATION A constant describing a linear acceleration sensor type. //线性加速度

int TYPE_MAGNETIC_FIELD A constant describing a magnetic field sensor type. //磁场感应 返回三个坐标轴的数值 微特斯拉

int TYPE_ORIENTATION This constant is deprecated. use SensorManager.getOrientation() instead. //方向感应器 已过时 可以使用方法获得

int TYPE_PRESSURE A constant describing a pressure sensor type //压力感应器 单位 千帕斯卡

int TYPE_PROXIMITY A constant describing an proximity sensor type. //距离传感器

int TYPE_ROTATION_VECTOR A constant describing a rotation vector sensor type. //翻转传感器

int TYPE_TEMPERATURE A constant describing a temperature sensor type //温度传感器 单位 摄氏度

此类中包含的方法都是get型的 用来获取所选sensor的一些属性,sensor类一般不需要new而是通过SensorManager的方法获得

 

二 介绍SensorManager类

 

SDK解释:“SensorManager lets you access the device's sensors. Get an instance of this class by calling Context.getSystemService() with the argument SENSOR_SERVICE.
Always make sure to disable sensors you don't need, especially when your activity is paused. Failing to do so can drain the battery in just a few hours. Note that the system will not disable sensors automatically when the screen turns off. ”

SensorManager 允许你访问设备的感应器。通过传入参数SENSOR_SERVICE参数调用Context.getSystemService方法可以获得一个sensor的实例。永远记得确保当你不需要的时候,特别是Activity暂定的时候,要关闭感应器。忽略这一点肯能导致几个小时就耗尽电池,注意当屏幕关闭时,系统不会自动关闭感应器。

 

三 代码实现

// 获得传感器管理器 

        if(sensorManager==null){ 

            sensorManager = (SensorManager) context 

            .getSystemService(Context.SENSOR_SERVICE);   

        } 

        if (sensorManager != null&&sensor==null) { 

            // 获得重力传感器 

            sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 

        } 

        if (sensor != null) { 

            sensorManager.registerListener(this, sensor, 

                    SensorManager.SENSOR_DELAY_NORMAL); 

        } 

//监听

 

private final SensorEventListener mSensorListener = new SensorEventListener() {

 

        private float x, y, z;

 

        @Override

        public void onSensorChanged(SensorEvent event) {

            // TODO Auto-generated method stub

            synchronized (mSensorManager) {

                switch (event.sensor.getType()) {

                    case Sensor.TYPE_ACCELEROMETER:

                        x = event.values[0];

                        y = event.values[1];

                        z = event.values[2];

                        if (z > 9) {

                            /* 正面向上,改为正常模式 */

                        } else if (z < -9) {

                            /* 正面向下*/

                           

                        } else if (x > 9) {

                            /* 正面向左 */

                        } else if (x < -9) {

                            /* 正面向右 */

                        } else if (y > 9) {

                            /* 手掌正翻向自己直立 */

                        } else if (y < -9) {

                            /* 手掌反翻背向自己直立 */

                        } else {

                            /* 无法判断情况 */

                        }

                        break;

                }

            }

        }

 

        @Override

        public void onAccuracyChanged(Sensor sensor, int accuracy) {

            // TODO Auto-generated method stub

        }

 

};

 

需要注意的是,对手机的移动操作实现对于手机屏幕朝向为标准,例如手机屏幕向左,此时认为手机向左移动。

以上是手机为纵向屏幕时的坐标,如果当前手机是横向屏幕:x>0 说明当前手机下翻 x<0上翻; y>0 说明当前手机右翻 y<0左翻;z轴坐标不变。

下面说一下角度获取,虽然可以使用ORIENTATION去获取,但是那个也不太好用,因为它是以向北为标准而计算的Sensor Event 所提供的加速度数值,是设备以地球为参照物的加速度减去重力加速度的叠加后的值。我是这样理解的:当以重力加速度g向地面作自由落体运动时,手机处于失重状态,g-sensor以这种状态作为加速度的0;而当手机处于静止状态(相对于地面)时,为了抵御自由落体运动的趋势,它有一个反向(向上)的g的加速度。

因此,得出一个结论:当设备处于静止或者匀速运动状态时,它有一个垂直地面向上的g的加速度,这个g投影到设备坐标系的x、y、z轴上,就是SensorEvent 提供给我们的3个分量的数值。在“设备处于静止或者匀速运动状态”的假设的前提下,可以根据SensorEvent所提供的3个加速度分量计算出设备相对于地面的方向前面所提到的“设备的方向”是一个含糊的说法。这里我们精确地描述设备方向为:以垂直于地面的方向为正方向,用设备坐标系x、y、z轴与正方向轴之间的夹角Ax、Ay、Az来描述设备的方向,如下图所示。可以看出,设备还有一个自由度,即:绕着正方向轴旋转,Ax、Ay、Az不变。但Ax、Ay、Az的约束条件,对于描述设备相对于正方向轴的相对位置已经足够了。如果需要完全约束设备相对于地面的位置,除了正方向轴外,还需要引入另一个参照轴,例如连接地球南、北极的地轴(如果设备上有地磁强度Sensor,则可满足该约束条件)

Ax、Ay、Az的范围为[0, 2*PI)。例如,当Ay=0时,手机y轴竖直向上;Ay=PI(PI:Android 中代码为Math.PI,也就是π)时,手机y轴向下;Ay=PI/2时,手机水平、屏幕向上;Ay=3*PI/2时,手机水平、屏幕向下。

根据3D矢量代数的法则,可知:

  • Gx=g*cos(Ax)
  • Gy=g*cos(Ay)
  • Gz=g*cos(Az)
  • g^2=Gz^2+Gy^2+Gz^2

因此,根据Gx、Gy、Gz,可以计算出Ax、Ay、Az

在x-y平面上的2D简化

当Ax、Ay确定时,Az有两种可能的值,二者相差PI,确定了设备屏幕的朝向是向上还是向下。大多数情况下,我们只关心Ax、Ay(因为程序UI位于x-y平面?),而忽略Az,例如,Android的屏幕自动旋转功能,不管使用者是低着头看屏幕(屏幕朝上)、还是躺在床上看(屏幕朝下),UI始终是底边最接近地心的方向

那么我们设Gx与Gy的矢量和为g'(即:g在x-y平面上的投影),将计算简化到x-y 2D平面上。记y轴相对于g'的偏角为A,以A来描述设备的方向。以逆时针方向为正,A的范围为[0, 2*PI)

有:

  • g'^2=Gx^2+Gy^2
  • Gy=g'*cos(A)
  • Gx=g'*sin(A)

则:

  • g'=sqrt(Gx^2+Gy^2)
  • A=arccos(Gy/g')

由于arccos函数值范围为[0, PI];而A>PI时,Gx=g'*sin(A)<0,因此,根据Gx的符号分别求A的值为:

  • 当Gx>=0时,A=arccos(Gy/g')
  • 当Gx<0时,A=2*PI-arccos(Gy/g')

注意:由于cos函数曲线关于直线x=n*PI 对称,因此arccos函数的曲线如果在y轴方向[0, 2*PI]范围内补全的话,则关于直线y=PI对称,因此有上面当Gx<0时的算法

 

猜你喜欢

转载自blog.csdn.net/greatdaocaoren/article/details/97111702