利用GSensor讓屏幕實現360度旋轉

Android的 Settings->Sound and Display中有 Orientation這一設置項。當選中時,反轉手機,手機屏幕會隨之旋轉,一般只可以旋轉90度。

這一 settings設置是在文件 SoundAndDisplaySettings.java中,該項對應的鍵字符串爲:

  1. private   static   final  String KEY_ACCELEROMETER =  "accelerometer" ;  
複製代碼
Java代碼
  1. private static final String KEY_ACCELEROMETER = "accelerometer";
複製代碼

其默認值保存在 xml文件中,默認是 Enable。 UI程序初始化時會根據其值是否在複選框中打勾(代碼在 onCreate函數中):

  1.    protected   void  onCreate(Bundle savedInstanceState) {  
  2. …  
  3.         mAccelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER);  
  4.         mAccelerometer.setPersistent(false );  
  5. …  
  6. }  
複製代碼
Java代碼
  1.   protected void onCreate(Bundle savedInstanceState) {  
  2. …  
  3.         mAccelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER);  
  4.         mAccelerometer.setPersistent(false);  
  5. …  
  6. }  
複製代碼



當用戶改變了該值時,會保存起來:



Java代碼

  1. public   boolean  onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {  
  2. …  
  3.     } else   if  (preference == mAccelerometer) {  
  4.             Settings.System.putInt(getContentResolver(),  
  5.                     Settings.System.ACCELEROMETER_ROTATION,  
  6.                     mAccelerometer.isChecked() ? 1  :  0 );  
  7. …  
  8.         }  
複製代碼
  1. public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {  
  2. …  
  3.     } else if (preference == mAccelerometer) {  
  4.             Settings.System.putInt(getContentResolver(),  
  5.                     Settings.System.ACCELEROMETER_ROTATION,  
  6.                     mAccelerometer.isChecked() ? 1 : 0);  
  7. …  
  8.         }  
複製代碼

文件 frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java中的 SettingsServer會隨時監控其值,對用戶設置做出反應:



  1. public void update() {  
  2.             ContentResolver resolver = mContext.getContentResolver();  
  3.             boolean updateRotation = false ;  
  4.             synchronized (mLock) {  
  5.                 …  
  6.                 int accelerometerDefault = Settings.System.getInt(resolver,  
  7.                         Settings.System.ACCELEROMETER_ROTATION, DEFAULT_ACCELEROMETER_ROTATION);  
  8.                 if (mAccelerometerDefault != accelerometerDefault) {  
  9.                     mAccelerometerDefault = accelerometerDefault;  
  10.                     updateOrientationListenerLp();  
  11.                 }  
  12. …  
  13. }  
複製代碼

Java代碼

  1. publicvoid update() {  
  2.             ContentResolver resolver = mContext.getContentResolver();  
  3.             boolean updateRotation = false;  
  4.             synchronized (mLock) {  
  5.                 …  
  6.                 int accelerometerDefault = Settings.System.getInt(resolver,  
  7.                         Settings.System.ACCELEROMETER_ROTATION, DEFAULT_ACCELEROMETER_ROTATION);  
  8.                 if (mAccelerometerDefault != accelerometerDefault) {  
  9.                     mAccelerometerDefault = accelerometerDefault;  
  10.                     updateOrientationListenerLp();  
  11.                 }  
  12. …  
  13. }  
複製代碼

上述是設置生效流程。當 Orientation設置 Enable時,會發生什麼呢?

在 PhoneWindowManager.java有個 Listener,它會根據 Sensor判別出的旋轉方向,調用 WindowManager.setRotation讓屏幕進行旋轉。另外,當應用程序顯示禁止屏幕旋轉時則不會旋轉,見函數 needSensorRunningLp()。


  1. class MyOrientationListener extends WindowOrientationListener {  
  2.       MyOrientationListener(Context context) {  
  3.           super (context);  
  4.       }  

  5.       @Override
  6.       public void onOrientationChanged( int rotation) {  
  7.           // Send updates based on orientation value
  8.           if ( true ) Log.i(TAG, "onOrientationChanged, rotation changed to " +rotation);  
  9.           try {  
  10.               mWindowManager.setRotation(rotation, false ,  
  11.                       mFancyRotationAnimation);  
  12.           } catch (RemoteException e) {  
  13.               // Ignore
  14.           }  
  15.       }                                       
  16.   }  
  17.   MyOrientationListener mOrientationListener;  
  18.   boolean useSensorForOrientationLp( int appOrientation) {  
  19.       if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {  
  20.           return true ;  
  21.       }  
  22.       if (mAccelerometerDefault != 0 && (  
  23.               appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER ||  
  24.               appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) {  
  25.           return true ;  
  26.       }  
  27.       return false ;  
  28.   }  

  29.   /*  
  30.    * We always let the sensor be switched on by default except when  
  31.    * the user has explicitly disabled sensor based rotation or when the  
  32.    * screen is switched off.  
  33.    */
  34.   boolean needSensorRunningLp() {  
  35.       if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {  
  36.           // If the application has explicitly requested to follow the
  37.           // orientation, then we need to turn the sensor or.
  38.           return true ;  
  39.       }  
  40.       if (mAccelerometerDefault == 0 ) {  
  41.           // If the setting for using the sensor by default is enabled, then
  42.           // we will always leave it on.  Note that the user could go to
  43.           // a window that forces an orientation that does not use the
  44.           // sensor and in theory we could turn it off... however, when next
  45.           // turning it on we won't have a good value for the current
  46.           // orientation for a little bit, which can cause orientation
  47.           // changes to lag, so we'd like to keep it always on.  (It will
  48.           // still be turned off when the screen is off.)
  49.           return false ;  
  50.       }  
  51.       return true ;  
  52.   }
複製代碼

Java代碼


  1. class MyOrientationListener extends WindowOrientationListener {  
  2.       MyOrientationListener(Context context) {  
  3.           super(context);  
  4.       }  

  5.       @Override
  6.       publicvoid onOrientationChanged(int rotation) {  
  7.           // Send updates based on orientation value
  8.           if (true) Log.i(TAG, "onOrientationChanged, rotation changed to " +rotation);  
  9.           try {  
  10.               mWindowManager.setRotation(rotation, false,  
  11.                       mFancyRotationAnimation);  
  12.           } catch (RemoteException e) {  
  13.               // Ignore
  14.           }  
  15.       }                                       
  16.   }  
  17.   MyOrientationListener mOrientationListener;  
  18.   boolean useSensorForOrientationLp(int appOrientation) {  
  19.       if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {  
  20.           returntrue;  
  21.       }  
  22.       if (mAccelerometerDefault != 0 && (  
  23.               appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER ||  
  24.               appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) {  
  25.           returntrue;  
  26.       }  
  27.       returnfalse;  
  28.   }  

  29.   /*
  30.    * We always let the sensor be switched on by default except when
  31.    * the user has explicitly disabled sensor based rotation or when the
  32.    * screen is switched off.
  33.    */
  34.   boolean needSensorRunningLp() {  
  35.       if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {  
  36.           // If the application has explicitly requested to follow the
  37.           // orientation, then we need to turn the sensor or.
  38.           returntrue;  
  39.       }  
  40.       if (mAccelerometerDefault == 0) {  
  41.           // If the setting for using the sensor by default is enabled, then
  42.           // we will always leave it on.  Note that the user could go to
  43.           // a window that forces an orientation that does not use the
  44.           // sensor and in theory we could turn it off... however, when next
  45.           // turning it on we won't have a good value for the current
  46.           // orientation for a little bit, which can cause orientation
  47.           // changes to lag, so we'd like to keep it always on.  (It will
  48.           // still be turned off when the screen is off.)
  49.           returnfalse;  
  50.       }  
  51.       returntrue;  
  52.   }  
複製代碼

在 WindowOrientationListener(見文件 javaframeworks/base/core/java/android/view/WindowOrientationListener.java)中會監聽Sensor的值,對旋轉方向進行判斷,然後調用抽象方法 onOrientationChanged,因此,只要在子類 Listener中重新實現這個函數即可對四個不同方向做出響應(見上面讓屏幕旋轉即是一例)。

遺憾的是在 Donut和 Éclair中,對旋轉方向的識別只給出了 90度旋轉,在 Froyo中增加了一個 270度旋轉,不支持 180度即倒立的操作。

可以在修改下面代碼,判斷來自於 Gsensor的值,識別出旋轉方向:


  1. public void onSensorChanged(SensorEvent event) {  
  2.     float [] values = event.values;  
  3.     float X = values[_DATA_X];  
  4.     float Y = values[_DATA_Y];  
  5.     float Z = values[_DATA_Z];  
  6.     float OneEightyOverPi = 57 .29577957855f;  
  7.     float gravity = ( float ) Math.sqrt(X*X+Y*Y+Z*Z);  
  8.     float zyangle = ( float )Math.asin(Z/gravity)*OneEightyOverPi;  
  9.     int rotation = - 1 ;  
  10.     if ((zyangle <= PIVOT_UPPER) && (zyangle >= PIVOT_LOWER)) {  
  11.         // Check orientation only if the phone is flat enough
  12.         // Don't trust the angle if the magnitude is small compared to the y value
  13.         float angle = ( float )Math.atan2(Y, -X) * OneEightyOverPi;  
  14.         int orientation = 90 - ( int )Math.round(angle);  
  15.         // normalize to 0 - 359 range
  16.         while (orientation >= 360 ) {  
  17.             orientation -= 360 ;  
  18.         }   
  19.         while (orientation < 0 ) {  
  20.             orientation += 360 ;  
  21.         }  
  22.         // Orientation values between  LANDSCAPE_LOWER and PL_LOWER
  23.         // are considered landscape.
  24.         // Ignore orientation values between 0 and LANDSCAPE_LOWER
  25.         // For orientation values between LP_UPPER and PL_LOWER,
  26.         // the threshold gets set linearly around PIVOT.
  27.         if ((orientation >= PL_LOWER) && (orientation <= LP_UPPER)) {  
  28.             float threshold;  
  29.             float delta = zyangle - PIVOT;  
  30.             if (mSensorRotation == Surface.ROTATION_90) {  
  31.                 if (delta < 0 ) {  
  32.                     // Delta is negative
  33.                     threshold = LP_LOWER - (LP_LF_LOWER * delta);  
  34.                 } else {  
  35.                     threshold = LP_LOWER + (LP_LF_UPPER * delta);  
  36.                 }  
  37.                 rotation = (orientation >= threshold) ? Surface.ROTATION_0 : Surface.ROTATION_90;  
  38.             } else {  
  39.                 if (delta < 0 ) {  
  40.                     // Delta is negative
  41.                     threshold = PL_UPPER+(PL_LF_LOWER * delta);  
  42.                 } else {  
  43.                     threshold = PL_UPPER-(PL_LF_UPPER * delta);  
  44.                 }  
  45.                 rotation = (orientation <= threshold) ? Surface.ROTATION_90: Surface.ROTATION_0;  
  46.             }  
  47.         } else if ((orientation >= LANDSCAPE_LOWER) && (orientation < LP_LOWER)) {  
  48.             rotation = Surface.ROTATION_90;  
  49.         } else if ((orientation >= PL_UPPER) || (orientation <= PORTRAIT_LOWER)) {  
  50.             rotation = Surface.ROTATION_0;  
  51.         }  
  52.         if ((rotation != - 1 ) && (rotation != mSensorRotation)) {  
  53.             mSensorRotation = rotation;  
  54.             onOrientationChanged(mSensorRotation);  
  55.         }  
  56.     }  
  57. }  
複製代碼


Java代碼


  1. publicvoid onSensorChanged(SensorEvent event) {  
  2.     float[] values = event.values;  
  3.     float X = values[_DATA_X];  
  4.     float Y = values[_DATA_Y];  
  5.     float Z = values[_DATA_Z];  
  6.     float OneEightyOverPi = 57.29577957855f;  
  7.     float gravity = (float) Math.sqrt(X*X+Y*Y+Z*Z);  
  8.     float zyangle = (float)Math.asin(Z/gravity)*OneEightyOverPi;  
  9.     int rotation = -1;  
  10.     if ((zyangle <= PIVOT_UPPER) && (zyangle >= PIVOT_LOWER)) {  
  11.         // Check orientation only if the phone is flat enough
  12.         // Don't trust the angle if the magnitude is small compared to the y value
  13.         float angle = (float)Math.atan2(Y, -X) * OneEightyOverPi;  
  14.         int orientation = 90 - (int)Math.round(angle);  
  15.         // normalize to 0 - 359 range
  16.         while (orientation >= 360) {  
  17.             orientation -= 360;  
  18.         }   
  19.         while (orientation < 0) {  
  20.             orientation += 360;  
  21.         }  
  22.         // Orientation values between  LANDSCAPE_LOWER and PL_LOWER
  23.         // are considered landscape.
  24.         // Ignore orientation values between 0 and LANDSCAPE_LOWER
  25.         // For orientation values between LP_UPPER and PL_LOWER,
  26.         // the threshold gets set linearly around PIVOT.
  27.         if ((orientation >= PL_LOWER) && (orientation <= LP_UPPER)) {  
  28.             float threshold;  
  29.             float delta = zyangle - PIVOT;  
  30.             if (mSensorRotation == Surface.ROTATION_90) {  
  31.                 if (delta < 0) {  
  32.                     // Delta is negative
  33.                     threshold = LP_LOWER - (LP_LF_LOWER * delta);  
  34.                 } else {  
  35.                     threshold = LP_LOWER + (LP_LF_UPPER * delta);  
  36.                 }  
  37.                 rotation = (orientation >= threshold) ? Surface.ROTATION_0 : Surface.ROTATION_90;  
  38.             } else {  
  39.                 if (delta < 0) {  
  40.                     // Delta is negative
  41.                     threshold = PL_UPPER+(PL_LF_LOWER * delta);  
  42.                 } else {  
  43.                     threshold = PL_UPPER-(PL_LF_UPPER * delta);  
  44.                 }  
  45.                 rotation = (orientation <= threshold) ? Surface.ROTATION_90: Surface.ROTATION_0;  
  46.             }  
  47.         } elseif ((orientation >= LANDSCAPE_LOWER) && (orientation < LP_LOWER)) {  
  48.             rotation = Surface.ROTATION_90;  
  49.         } elseif ((orientation >= PL_UPPER) || (orientation <= PORTRAIT_LOWER)) {  
  50.             rotation = Surface.ROTATION_0;  
  51.         }  
  52.         if ((rotation != -1) && (rotation != mSensorRotation)) {  
  53.             mSensorRotation = rotation;  
  54.             onOrientationChanged(mSensorRotation);  
  55.         }  
  56.     }  
  57. }  
複製代碼

在Froyo中,對上述算法進行了修改,讓其報出270度的旋轉方向。


Android Sensor 屏幕360度旋轉實現

修改下面函數


  1. void android.view.WindowOrientationListener SensorEventListenerImp.onSensorChanged(android.hardware.SensorEvent event)
  2.             float[] values = event.values;
  3.             
  4.             float X = values[_DATA_X];
  5.             float Y = values[_DATA_Y];
  6.             float Z = values[_DATA_Z];
  7.             //For fixing the problem of Sensor change the window orientation error but the sensor game is no problem.
  8.             float OneEightyOverPi = 57.29577957855f;
  9.             float gravity = (float) Math.sqrt(X*X+Y*Y+Z*Z);
  10.             float zyangle = (float)Math.asin(Z/gravity)*OneEightyOverPi;
  11.             int rotation = -1;
  12.             if ((zyangle <= PIVOT_UPPER) && (zyangle >= PIVOT_LOWER)) {
  13.                 // Check orientation only if the phone is flat enough
  14.                 // Don't trust the angle if the magnitude is small compared to the y value
  15.                 float angle = (float)Math.atan2(Y, -X) * OneEightyOverPi;
  16.                 int orientation = 90 - (int)Math.round(angle);
  17.                 // normalize to 0 - 359 range
  18.                 while (orientation >= 360) {
  19.                     orientation -= 360;
  20.                 }
  21.                 while (orientation < 0) {
  22.                     orientation += 360;
  23.                 }
  24.                 Log.i("Tiger","orientation="+orientation);
  25.                  //確定由角度與屏幕方向的對應範圍
  26.                 if(orientation > 325 || orientation <= 45){
  27.                 rotation = Surface.ROTATION_0;
  28.                 }else if(orientation > 45 && orientation <= 135){
  29.                 rotation = Surface.ROTATION_270;
  30.                 }else if(orientation > 135 && orientation < 225){
  31.                 rotation = Surface.ROTATION_180;
  32.                 }else {
  33.                 rotation = Surface.ROTATION_90;
  34.                 }
  35.                
  36.                 Log.i("Tiger","mSensorRotation="+mSensorRotation+"    , rotation="+rotation);
  37.                 if ((rotation != -1) && (rotation != mSensorRotation)) {
  38.                     mSensorRotation = rotation;
  39.                     onOrientationChanged(mSensorRotation);
  40.                 }
  41.             }
複製代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章