基本思路:在输入事件分发线程中(代码在WindowManagerService.java中),当事件为按键事件而且按键码与自定义功能键的码值一样时,向一个特定的Service(如com.android.settings.ButtonService)来请求服务。示例代码为

If (code == KeyEvent.KEYCODE_***)

mContext.startService(newIntent(“com.android.settings.ButtonService”)); //当该service对象不存在时,系统将会创建一个service实例对象来响应该请求,此时会执行service的onCreate( )方法,在执行onStartCommand( )方法,若已存在一个该service的实例对象时,只执行onStartCommand( )方法。

在Service的onStartCommand( )方法中,如果执行一个时间较长的任务,则会阻塞该service所在的线程,如果没有指定service的android:process属性,则service运行在UI线程中,会出现ANR的错误,一般解决方法为new一个Thread来解决阻塞问题。在本示例中只需要改变一个ContentProvider中所存储的一个key-value的值,该值相当于全局标志,当重力传感器的值发生变化需要进行屏幕旋转之前先检查ContentProvider中的标志值,如果未设置,则执行屏幕旋转动作,否则不执行,(不同应用程序之间共享数据由ContentProvider类来提供服务)。每次按下功能键时,先判定按键禁止屏幕的选项有没有开启,开启则反转ContentProvider中标志值,未开启就直接退出,判定选项有没有开启的方法也是设置一个标志值,当选项选中时,置位标志值,取消时,复位标志值,并将ContentProvider中的标志值也复位。

控制手机屏幕旋转的关键类为WindowOrientationListener, 该类为抽象类,系统中唯一实现继续该类的为PhoneWindowManager类,在WindowOrientationListener类的构造函数中,会获取一个SensorManager类的实例对象,通过SensorManager获取一个加速度传感器对象,最后new一个SensorEventListener。代码为:

mSensorManager=(SensorManager)context.getSystemService(Context.SENSOR_SERVICE);

mRate = rate;

mSensor =mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

if (mSensor != null) {

// Create listener only if sensors do exist

mSensorEventListener = newSensorEventListenerImpl();

}

在WindowOrientationListener类的enable方法中,向SensorManager中注册一个传感器事件监听器。

/**

* Enables theWindowOrientationListener so it will monitor the sensor and call

* {@link#onOrientationChanged} when the device orientation changes.

*/

public void enable() {

if (mSensor == null) {

Log.w(TAG,"Cannot detect sensors. Not enabled");

return;

}

if (mEnabled == false){

if (localLOGV)Log.d(TAG, "WindowOrientationListener enabled");

mSensorManager.registerListener(mSensorEventListener,mSensor, mRate);

mEnabled = true;

}

}

在WindowOrientationListener类中有一个实现了SensorEventListener接口的SensorEventListenerImpl的内部类,当传感器的数据发生变化时,会调用其onSensorChanged方法,该方法的实现主要是将加速度传感器上报的数据转换为设备旋转的方位角,然后在调用calculateNewRotation方法将方位角确定为屏幕的旋转方向,其实现主要是利用WindowOrientationListener类里的两个数组:THRESHOLDS和ROTATE_TO数组来实现确定屏幕的旋转方向。示例代码为:

private int[][][]THRESHOLDS = new int[][][] {

{{79, 180}, {180, 282}},

{{0, 45}, {45, 165}, {300,360}},

{{0, 53}, {195, 315}, {315,360}}

};

// See THRESHOLDS

private final int[][] ROTATE_TO = new int[][]{

{ROTATION_270, ROTATION_90},

{ROTATION_0, ROTATION_270,ROTATION_0},

{ROTATION_0, ROTATION_90,ROTATION_0}

};

这两个数组是一一对应的,THRESHOLDS数组为三维数组(可看作一个一维数组,数组中的元素类型为二维数组),THRESHOLDS数组第一行代表当手机屏幕处于ROTATION_0时,当手机旋转到79~180度,calculateNewRotation将根据相应的ROTATE_TO数组计算得出屏幕该旋转的方向为ROTATION_270, 旋转到180~282度时,同理得出屏幕该旋转的方向为ROTATION_90。THRESHOLDS数组第二行代表当手机屏幕处于ROTATION_90,如果屏幕旋转的角度在所定义的角度范围内,屏幕将会旋转,最终的旋转方向取决于ROTATE_TO数组的第二行,依次类推,第三行对应ROTATION_270,如果要支持ROTATION_180,则只需要在两个数组中添加相应的元素即可。

当calculateNewRotation计算出应该旋转的方位时,会将结果传递给onOrientationChanged( ),该方法为抽象方法,需要继承的子类来实现,系统中WindowOrientaionListener唯一继承该子类为PhoneWindowManager类的一个内部类MyOrientationListener类,在该类的onOrientationChanged方法的实现中,将会调用WindowManagerService类的一个实例对象的setRotation方法。

class MyOrientationListener extends WindowOrientationListener {

//构造函数

MyOrientationListener(Context context){

super(context);

}

@Override

public void onOrientationChanged(introtation) {

// Send updates based onorientation value

if (localLOGV) Log.v(TAG,"onOrientationChanged, rotation changed to " +rotation);

try {

mWindowManager.setRotation(rotation, false,

mFancyRotationAnimation);

} catch (RemoteException e) {

// Ignore

}

}

}

在WindowManagerService的setRotation方法中实现屏幕的旋转和view树的重绘。其主要实现是Surface.setOrientation方法的调用。

知道了屏幕旋转的核心代码,禁止屏幕旋转的方法就很简单了,只要在mWindowManager.setRotation(rotation, false, mFancyRotationAnimation)方法调用之用加入一个条件语句就OK了,设置一个全局标志位,在功能键禁止/使能屏幕旋转选项没选中时,全局标志位设为0,当选项选中时,当功能键按下时,反转全局标志位的值就可以决定是否旋转屏幕。当选项没选中时,必须复位全局标志位,否则在将自动旋转屏幕和功能键禁止/使能选项全部选中时,如果按下功能键,此时将功能键的选项取消时,屏幕将不会在自动旋转。


更多相关文章

  1. android读取sd卡图片并进行缩放操作
  2. Android中快速为Recyclerview添加头部
  3. mysql错误:Access denied for user 'root'@'172.19.100.123' to d
  4. Android事件总线框架设计:EventBus3.0源码详解与架构分析(中)
  5. android 判断手机是否是国内的手机的方法
  6. Android(安卓)ViewPager切换的N种动画
  7. Android(安卓)touch事件的派发流程
  8. android kotlin 学习笔记基础篇(一)
  9. 下载最新android adt的方法

随机推荐

  1. Android,visibility属性
  2. Android 自定义字体
  3. Android之MediaScanner流程
  4. Could not get resource ‘https://dl.go
  5. android系统本身的图片资源
  6. Android Bluetooth蓝牙名称修改
  7. Android: 上传图片到服务器
  8. 【Android Studio】Android Studio和Grad
  9. android获取短信验证码自动填入
  10. android manifest