作者: 声网Agora 工程师 黄龙飞

前言
在日常生活中应用手机,通常都会遇到上面这两种场景。
场景一:
在应用手机看视频且设施开启屏幕主动旋转时,手机横着拿和竖着拿,所看到的成果会不一样。竖屏状态下的展现如下图(图1)所示,横屏状态下的展现如图2所示。

图1.竖屏播放视频

图2.横屏播放视频

场景二:
在应用的手机利用中,某些利用的某些界面会依据以后手机横竖屏的状态,展现不同的界面成果,不便大家应用。比方AgoraVideoCall中的会议界面,具体如图3 & 图4所示:

图3.AgoraVideoCall会议界面竖屏展现

图4.AgoraVideoCall会议界面横屏展现

上述中的两种场景,都是依据用户手持手机的形式及旋转动作自动识别出用户是想要横屏展现还是竖屏展现的。对于“手机如何依据用户的旋转动作而辨认出用户用意"这个问题将会在下文中具体论述。

加速度传感器原理
手机实现这一性能的核心部件是加速度传感器,在介绍加速度传感器之前,先理解一下传感器坐标系。

  • 传感器坐标系
通常,传感器框架应用规范的 3 轴坐标系来示意数据值。对于大多数传感器,当设施处于默认屏幕方向时,会绝对于设施屏幕来定义坐标系(参见图 5)。当设施处于默认屏幕方向时,X 轴为程度向右延长,Y 轴为垂直向上延长,Z 轴为垂直于屏幕向外延长。在此坐标系中,屏幕前面的坐标将具备负 Z 值。对于此坐标系,特地须要留神的一点就是传感器的坐标系不会随着设施的挪动而扭转。

图5.传感器坐标系(绝对于设施)。

  • 加速度传感器
加速度传感器,它采纳弹性敏感元件制成悬臂式位移器,与采纳弹性敏感元件制成的储能弹簧来驱动电触点,实现从重力变动到电信号的转换。例如:一个壳体与要测量加速度的物体,通过弹簧连贯在一起,组成的一个重力感应器,当咱们把壳体向上挪动时,金属球会因为惯性向下拉伸弹簧,这时咱们只须要测量出弹簧的拉伸量,咱们就能够由此计算出重力。由此易得,X,Y,Z加速度计,就能测量一个物体在三维空间中的静止方向。具体阐明请参阅[重力感应原理]

加速度传感器在Android横竖屏切换中的利用
加速度传感器在挪动设施中的利用泛滥,本文次要介绍加速度传感器在Android零碎中横竖屏切换的利用,其原理次要为:通过监听加速度传感器,实时取得X、Y、Z三个方向的加速度值;当4(X_X + Y_Y)>=Z*Z时,开始计算设施在X与Y立体上的旋转角度;最初依据旋转角度计算出设施的横竖屏状态。上面贴出次要代码。

/*** 在onResume中,向SensorManager注册监听加速度传感器*/@Overrideprotected void onResume() {    super.onResume();    orientationListener = new OrientationListener(this);    orientationListener.enable();}public void enable() {    if (mSensor == null) {        Log.w(TAG, "Cannot detect sensors. Not enabled");        return;    }    if (mEnabled == false) {        if (localLOGV) Log.d(TAG, "OrientationEventListener enabled");        mSensorManager.registerListener(mSensorEventListener, mSensor, mRate);        mEnabled = true;    }} 

class SensorEventListenerImpl implements SensorEventListener {    private static final int _DATA_X = 0;    private static final int _DATA_Y = 1;    private static final int _DATA_Z = 2;    /**    ** 通过X、Y、Z三个方向的加速度值的变动,计算出设施旋转的角度。    **/    public void onSensorChanged(SensorEvent event) {        float[] values = event.values;        int orientation = ORIENTATION_UNKNOWN;        float X = -values[_DATA_X];        float Y = -values[_DATA_Y];        float Z = -values[_DATA_Z];                float magnitude = X*X + Y*Y;        // Don't trust the angle if the magnitude is small compared to the y value        if (magnitude * 4 >= Z*Z) {            float OneEightyOverPi = 57.29577957855f;            float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi;            orientation = 90 - (int)Math.round(angle);            // normalize to 0 - 359 range            while (orientation >= 360) {                orientation -= 360;            }             while (orientation < 0) {                orientation += 360;            }        }        if (mOldListener != null) {            mOldListener.onSensorChanged(Sensor.TYPE_ACCELEROMETER, event.values);        }        if (orientation != mOrientation) {            mOrientation = orientation;            onOrientationChanged(orientation);        }    }} 

public class OrientationListener extends OrientationEventListener {    private int degree = 0;//旋转角度    private int mOrientation = 0;//2—横屏 1-竖屏,0-未知    public OrientationListener(Context context) {        super(context);    }    /**    * 依据旋转的角度,得出设施横竖屏状态    **/    @Override    public void onOrientationChanged(int orientation) {        degree = orientation;        if (degree > 0 && degree < 45) {            mOrientation = 1;        } else if (degree > 45 && degree < 135) {            mOrientation = 2;        } else if (degree > 135 && degree < 225) {            mOrientation = 1;        } else if (degree > 225 && degree < 315) {            mOrientation = 2;        } else if (degree > 315 && degree < 360) {            mOrientation = 1;        }         if (mOrientation == 2) {            Log.i("OrientationListener ", "横屏");        } else if (mOrientation == 1) {            Log.i("OrientationListener ", "竖屏");        }    }} 

特地阐明:Math.atan2(-Y, X) 是计算从原点(0,0)到(x,y)点的线段与x轴正方向之间的立体角度(弧度值),
float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi失去的是原点(0,0)到(x,y)点的线段与x轴正方向之间的角度,90 - (int)Math.round(angle)为设施旋转的角度。

实战演练
本节实现一个繁难的打高尔夫球游戏,练习一下加速度传感器的使用。该游戏为多人游戏,游戏规则为:参与者通过摇摆手机管制高尔夫球的挪动,当高尔夫球落到洞中则计1分,否则不计分,每人操作3次,得分最高者获胜。游戏实现原理是依据加速度传感器取得设施旋转角度,实时计算并更新高尔夫球的地位,依据高尔夫球的地位与洞的重合度,判断高尔夫球是否落入洞中。落入洞中则得分,否则,不计分。游戏具体实现见附件,演示如下。

链接: https://pan.baidu.com/s/1Kas3kL0fdaXbygGA--l7JQ 2 提取码: 87nh
链接: https://pan.baidu.com/s/1yXNbbI3QhYG3BMZsyG2PSw 1 提取码: 8uaz

扩大
大多数挪动设施,除了下面介绍的加速度传感器外,还有很多内置传感器,比方:重力传感器、旋转矢量传感器、屏幕方向传感器、温度传感器、光传感器、压力传感器等(如需理解详细信息,请参阅传感器)。开发者能够依据这些传感器,实现许多十分智能的性能。比方:

  • 通过光线传感器,主动调节屏幕的亮度,爱护用户的眼睛
  • 通过加速度传感器,实现计步器和静止检测性能
  • 通过湿度传感器和温度传感器,计算露点和绝对湿度
  • 通过GPS,实现导航性能

参考文献:

  • https://developer.android.com/guide/topics/sensors/sensors_motion?hl=zh-cn 1
  • https://developer.android.com/guide/topics/sensors/sensors_overview?hl=zh-cn
  • https://github.com/googlearchive/android-AccelerometerPlay
  • https://v.qq.com/x/page/t050708r6ea.html
  • https://baike.baidu.com/item/重力感应器?fromtitle=重力传感器&fromid=3623984

附件
AccelerometerPlay.zip (5.0 KB)