http://www.iteye.com/wiki/blog/812749 在Android的Settings->Sound andDisplay中有Orientation这一设置项。当选中时,反转手机,手机屏幕会随之旋转,一般只可以旋转90度。 这一settings设置是在文件SoundAndDisplaySettings.java中,该项对应的键字符串为: view plaincopy to clipboardprint? private static final String KEY_ACCELEROMETER = "accelerometer"; Java代码 private static final String KEY_ACCELEROMETER = "accelerometer"; 其默认值保存在xml文件中,默认是Enable。UI程序初始化时会根据其值是否在复选框中打勾(代码在onCreate函数中): view plaincopy to clipboardprint? protected void onCreate(Bundle savedInstanceState) { … mAccelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER); mAccelerometer.setPersistent(false); … } Java代码 protected void onCreate(Bundle savedInstanceState) { … mAccelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER); mAccelerometer.setPersistent(false); … } 当用户改变了该值时,会保存起来: view plaincopy to clipboardprint? public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { … } else if (preference == mAccelerometer) { Settings.System.putInt(getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, mAccelerometer.isChecked() ? 1 : 0); … } Java代码 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { … } else if (preference == mAccelerometer) { Settings.System.putInt(getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, mAccelerometer.isChecked() ? 1 : 0); … } 文件frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java中的SettingsServer会随时监控其值,对用户设置做出反应: view plaincopy to clipboardprint? public void update() { ContentResolver resolver = mContext.getContentResolver(); boolean updateRotation = false; synchronized (mLock) { … int accelerometerDefault = Settings.System.getInt(resolver, Settings.System.ACCELEROMETER_ROTATION, DEFAULT_ACCELEROMETER_ROTATION); if (mAccelerometerDefault != accelerometerDefault) { mAccelerometerDefault = accelerometerDefault; updateOrientationListenerLp(); } … } Java代码 public void update() { ContentResolver resolver = mContext.getContentResolver(); boolean updateRotation = false; synchronized (mLock) { … int accelerometerDefault = Settings.System.getInt(resolver, Settings.System.ACCELEROMETER_ROTATION, DEFAULT_ACCELEROMETER_ROTATION); if (mAccelerometerDefault != accelerometerDefault) { mAccelerometerDefault = accelerometerDefault; updateOrientationListenerLp(); } … } 上述是设置生效流程。当Orientation设置Enable时,会发生什么呢? 在PhoneWindowManager.java有个Listener,它会根据Sensor判别出的旋转方向,调用WindowManager.setRotation让屏幕进行旋转。另外,当应用程序显示禁止屏幕旋转时则不会旋转,见函数needSensorRunningLp()。 view plaincopy to clipboardprint? class MyOrientationListener extends WindowOrientationListener { MyOrientationListener(Context context) { super(context); } @Override public void onOrientationChanged(int rotation) { // Send updates based on orientation value if (true) Log.i(TAG, "onOrientationChanged, rotation changed to " +rotation); try { mWindowManager.setRotation(rotation, false, mFancyRotationAnimation); } catch (RemoteException e) { // Ignore } } } MyOrientationListener mOrientationListener; boolean useSensorForOrientationLp(int appOrientation) { if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) { return true; } if (mAccelerometerDefault != 0 && ( appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) { return true; } return false; } /* * We always let the sensor be switched on by default except when * the user has explicitly disabled sensor based rotation or when the * screen is switched off. */ boolean needSensorRunningLp() { if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) { // If the application has explicitly requested to follow the // orientation, then we need to turn the sensor or. return true; } if (mAccelerometerDefault == 0) { // If the setting for using the sensor by default is enabled, then // we will always leave it on. Note that the user could go to // a window that forces an orientation that does not use the // sensor and in theory we could turn it off... however, when next // turning it on we won't have a good value for the current // orientation for a little bit, which can cause orientation // changes to lag, so we'd like to keep it always on. (It will // still be turned off when the screen is off.) return false; } return true; } Java代码 class MyOrientationListener extends WindowOrientationListener { MyOrientationListener(Context context) { super(context); } @Override public void onOrientationChanged(int rotation) { // Send updates based on orientation value if (true) Log.i(TAG, "onOrientationChanged, rotation changed to " +rotation); try { mWindowManager.setRotation(rotation, false, mFancyRotationAnimation); } catch (RemoteException e) { // Ignore } } } MyOrientationListener mOrientationListener; boolean useSensorForOrientationLp(int appOrientation) { if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) { return true; } if (mAccelerometerDefault != 0 && ( appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) { return true; } return false; } /* * We always let the sensor be switched on by default except when * the user has explicitly disabled sensor based rotation or when the * screen is switched off. */ boolean needSensorRunningLp() { if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) { // If the application has explicitly requested to follow the // orientation, then we need to turn the sensor or. return true; } if (mAccelerometerDefault == 0) { // If the setting for using the sensor by default is enabled, then // we will always leave it on. Note that the user could go to // a window that forces an orientation that does not use the // sensor and in theory we could turn it off... however, when next // turning it on we won't have a good value for the current // orientation for a little bit, which can cause orientation // changes to lag, so we'd like to keep it always on. (It will // still be turned off when the screen is off.) return false; } return true; } 在WindowOrientationListener(见文件javaframeworks/base/core/java/android/view/WindowOrientationListener.java)中会监听Sensor的值,对旋转方向进行判断,然后调用抽象方法onOrientationChanged,因此,只要在子类Listener中重新实现这个函数即可对四个不同方向做出响应(见上面让屏幕旋转即是一例)。 遗憾的是在Donut和Éclair中,对旋转方向的识别只给出了90度旋转,在Froyo中增加了一个270度旋转,不支持180度即倒立的操作。 可以在修改下面代码,判断来自于Gsensor的值,识别出旋转方向: view plaincopy to clipboardprint? public void onSensorChanged(SensorEvent event) { float[] values = event.values; float X = values[_DATA_X]; float Y = values[_DATA_Y]; float Z = values[_DATA_Z]; float OneEightyOverPi = 57.29577957855f; float gravity = (float) Math.sqrt(X*X+Y*Y+Z*Z); float zyangle = (float)Math.asin(Z/gravity)*OneEightyOverPi; int rotation = -1; if ((zyangle <= PIVOT_UPPER) && (zyangle >= PIVOT_LOWER)) { // Check orientation only if the phone is flat enough // Don't trust the angle if the magnitude is small compared to the y value float angle = (float)Math.atan2(Y, -X) * OneEightyOverPi; int orientation = 90 - (int)Math.round(angle); // normalize to 0 - 359 range while (orientation >= 360) { orientation -= 360; } while (orientation < 0) { orientation += 360; } // Orientation values between LANDSCAPE_LOWER and PL_LOWER // are considered landscape. // Ignore orientation values between 0 and LANDSCAPE_LOWER // For orientation values between LP_UPPER and PL_LOWER, // the threshold gets set linearly around PIVOT. if ((orientation >= PL_LOWER) && (orientation <= LP_UPPER)) { float threshold; float delta = zyangle - PIVOT; if (mSensorRotation == Surface.ROTATION_90) { if (delta < 0) { // Delta is negative threshold = LP_LOWER - (LP_LF_LOWER * delta); } else { threshold = LP_LOWER + (LP_LF_UPPER * delta); } rotation = (orientation >= threshold) ? Surface.ROTATION_0 : Surface.ROTATION_90; } else { if (delta < 0) { // Delta is negative threshold = PL_UPPER+(PL_LF_LOWER * delta); } else { threshold = PL_UPPER-(PL_LF_UPPER * delta); } rotation = (orientation <= threshold) ? Surface.ROTATION_90: Surface.ROTATION_0; } } else if ((orientation >= LANDSCAPE_LOWER) && (orientation < LP_LOWER)) { rotation = Surface.ROTATION_90; } else if ((orientation >= PL_UPPER) || (orientation <= PORTRAIT_LOWER)) { rotation = Surface.ROTATION_0; } if ((rotation != -1) && (rotation != mSensorRotation)) { mSensorRotation = rotation; onOrientationChanged(mSensorRotation); } } } Java代码 public void onSensorChanged(SensorEvent event) { float[] values = event.values; float X = values[_DATA_X]; float Y = values[_DATA_Y]; float Z = values[_DATA_Z]; float OneEightyOverPi = 57.29577957855f; float gravity = (float) Math.sqrt(X*X+Y*Y+Z*Z); float zyangle = (float)Math.asin(Z/gravity)*OneEightyOverPi; int rotation = -1; if ((zyangle <= PIVOT_UPPER) && (zyangle >= PIVOT_LOWER)) { // Check orientation only if the phone is flat enough // Don't trust the angle if the magnitude is small compared to the y value float angle = (float)Math.atan2(Y, -X) * OneEightyOverPi; int orientation = 90 - (int)Math.round(angle); // normalize to 0 - 359 range while (orientation >= 360) { orientation -= 360; } while (orientation < 0) { orientation += 360; } // Orientation values between LANDSCAPE_LOWER and PL_LOWER // are considered landscape. // Ignore orientation values between 0 and LANDSCAPE_LOWER // For orientation values between LP_UPPER and PL_LOWER, // the threshold gets set linearly around PIVOT. if ((orientation >= PL_LOWER) && (orientation <= LP_UPPER)) { float threshold; float delta = zyangle - PIVOT; if (mSensorRotation == Surface.ROTATION_90) { if (delta < 0) { // Delta is negative threshold = LP_LOWER - (LP_LF_LOWER * delta); } else { threshold = LP_LOWER + (LP_LF_UPPER * delta); } rotation = (orientation >= threshold) ? Surface.ROTATION_0 : Surface.ROTATION_90; } else { if (delta < 0) { // Delta is negative threshold = PL_UPPER+(PL_LF_LOWER * delta); } else { threshold = PL_UPPER-(PL_LF_UPPER * delta); } rotation = (orientation <= threshold) ? Surface.ROTATION_90: Surface.ROTATION_0; } } else if ((orientation >= LANDSCAPE_LOWER) && (orientation < LP_LOWER)) { rotation = Surface.ROTATION_90; } else if ((orientation >= PL_UPPER) || (orientation <= PORTRAIT_LOWER)) { rotation = Surface.ROTATION_0; } if ((rotation != -1) && (rotation != mSensorRotation)) { mSensorRotation = rotation; onOrientationChanged(mSensorRotation); } } } 在Froyo中,对上述算法进行了修改,让其报出270度的旋转方向。 Android Sensor 屏幕360度旋转实现 2010-09-17 10:51 修改下面函数 void android.view.WindowOrientationListener.SensorEventListenerImpl.onSensorChanged(android.hardware.SensorEvent event) float[] values = event.values; float X = values[_DATA_X]; float Y = values[_DATA_Y]; float Z = values[_DATA_Z]; //For fixing the problem of Sensor change the window orientation error but the sensor game is no problem. float OneEightyOverPi = 57.29577957855f; float gravity = (float) Math.sqrt(X*X+Y*Y+Z*Z); float zyangle = (float)Math.asin(Z/gravity)*OneEightyOverPi; int rotation = -1; if ((zyangle <= PIVOT_UPPER) && (zyangle >= PIVOT_LOWER)) { // Check orientation only if the phone is flat enough // Don't trust the angle if the magnitude is small compared to the y value float angle = (float)Math.atan2(Y, -X) * OneEightyOverPi; int orientation = 90 - (int)Math.round(angle); // normalize to 0 - 359 range while (orientation >= 360) { orientation -= 360; } while (orientation < 0) { orientation += 360; } Log.i("Tiger","orientation="+orientation); //确定由角度与屏幕方向的对应范围 if(orientation > 325 || orientation <= 45){ rotation = Surface.ROTATION_0; }else if(orientation > 45 && orientation <= 135){ rotation = Surface.ROTATION_270; }else if(orientation > 135 && orientation < 225){ rotation = Surface.ROTATION_180; }else { rotation = Surface.ROTATION_90; } Log.i("Tiger","mSensorRotation="+mSensorRotation+" , rotation="+rotation); if ((rotation != -1) && (rotation != mSensorRotation)) { mSensorRotation = rotation; onOrientationChanged(mSensorRotation); } } 分享到: