在 Android的 Settings->Sound and Display中有 Orientation這一設置項。當選中時,反轉手機,手機屏幕會隨之旋轉,一般只可以旋轉90度。
這一 settings設置是在文件 SoundAndDisplaySettings.java中,該項對應的鍵字符串爲:
- private static final String KEY_ACCELEROMETER = "accelerometer" ;
- private static final String KEY_ACCELEROMETER = "accelerometer";
其默認值保存在 xml文件中,默認是 Enable。 UI程序初始化時會根據其值是否在複選框中打勾(代碼在 onCreate函數中):
- protected void onCreate(Bundle savedInstanceState) {
- …
- mAccelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER);
- mAccelerometer.setPersistent(false );
- …
- }
- protected void onCreate(Bundle savedInstanceState) {
- …
- mAccelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER);
- mAccelerometer.setPersistent(false);
- …
- }
當用戶改變了該值時,會保存起來:
Java代碼
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
- …
- } else if (preference == mAccelerometer) {
- Settings.System.putInt(getContentResolver(),
- Settings.System.ACCELEROMETER_ROTATION,
- mAccelerometer.isChecked() ? 1 : 0 );
- …
- }
- 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會隨時監控其值,對用戶設置做出反應:
- 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代碼
- publicvoid 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()。
- 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
- publicvoid 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) {
- returntrue;
- }
- if (mAccelerometerDefault != 0 && (
- appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER ||
- appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) {
- returntrue;
- }
- returnfalse;
- }
- /*
- * 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.
- returntrue;
- }
- 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.)
- returnfalse;
- }
- returntrue;
- }
在 WindowOrientationListener(見文件 javaframeworks/base/core/java/android/view/WindowOrientationListener.java)中會監聽Sensor的值,對旋轉方向進行判斷,然後調用抽象方法 onOrientationChanged,因此,只要在子類 Listener中重新實現這個函數即可對四個不同方向做出響應(見上面讓屏幕旋轉即是一例)。
遺憾的是在 Donut和 Éclair中,對旋轉方向的識別只給出了 90度旋轉,在 Froyo中增加了一個 270度旋轉,不支持 180度即倒立的操作。
可以在修改下面代碼,判斷來自於 Gsensor的值,識別出旋轉方向:
- 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代碼
- publicvoid 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;
- }
- } elseif ((orientation >= LANDSCAPE_LOWER) && (orientation < LP_LOWER)) {
- rotation = Surface.ROTATION_90;
- } elseif ((orientation >= PL_UPPER) || (orientation <= PORTRAIT_LOWER)) {
- rotation = Surface.ROTATION_0;
- }
- if ((rotation != -1) && (rotation != mSensorRotation)) {
- mSensorRotation = rotation;
- onOrientationChanged(mSensorRotation);
- }
- }
- }
在Froyo中,對上述算法進行了修改,讓其報出270度的旋轉方向。
Android Sensor 屏幕360度旋轉實現
修改下面函數
- void android.view.WindowOrientationListener SensorEventListenerImp.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);
- }
- }