1. 程式人生 > >android之Display.getRotation()_感測器控制螢幕旋轉

android之Display.getRotation()_感測器控制螢幕旋轉

在看android自帶的samples原始碼裡面的AccelerometerPlayActivity時,看到下面這段程式碼,很不理解

public void onSensorChanged(SensorEvent event) {
	if(event.sensor.getType() != Sensor.TYPE_ACCELEROMETER){
		return;
	}
	switch (mDisplay.getRotation()) {
	    case Surface.ROTATION_0://手機處於正常狀態
                  mSensorX = event.values[0];
                mSensorY = event.values[1];
                break;
            case Surface.ROTATION_90://手機旋轉90度
                  mSensorX = -event.values[1];
                mSensorY = event.values[0];
                break;
            case Surface.ROTATION_180:
                mSensorX = -event.values[0];
                mSensorY = -event.values[1];
                break;
            case Surface.ROTATION_270:
                mSensorX = event.values[1];
                mSensorY = -event.values[0];
                break;
	}
}

經過查閱資料大體瞭解了
通過AndroidManifest.xml設定螢幕方向的話,安裝後就不能改變,而程式內部設定螢幕方向就不會有這個限制。主要靠這兩個API:getRequestedOrientation()和setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)這兩個API通過ActivityManagerService.java的轉換後,實際上都是呼叫的WindowManagerService的同名方法。

每個Activity在WindowManagerService端都有一個AppWindowToken做代表,而螢幕的方向資訊就儲存在這裡。
PhoneWindowManager會自動根據螢幕物理特性決定螢幕方向,看這段程式碼:

if (mPortraitRotation < 0) {  
    // Initialize the rotation angles for each orientation once.   
    Display d = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))  
            .getDefaultDisplay();  
    if (d.getWidth() > d.getHeight()) {  
        mPortraitRotation = Surface.ROTATION_90;  
        mLandscapeRotation = Surface.ROTATION_0;  
        mUpsideDownRotation = Surface.ROTATION_270;  
        mSeascapeRotation = Surface.ROTATION_180;  
    } else {  
        mPortraitRotation = Surface.ROTATION_0;  
        mLandscapeRotation = Surface.ROTATION_90;  
        mUpsideDownRotation = Surface.ROTATION_180;  
        mSeascapeRotation = Surface.ROTATION_270;  
    }  
}  

這裡的d.getWidth() 和 d.getHeight()得到的是物理螢幕的寬高。一般來說,平板和手機的是不一樣的。

平板是寬比高大(0度時位於landscape模式,右轉90度進入porit模式),手機是高比寬大(0度是位於porit模式,右轉90度進入landscape模式)。如果應用程式只關心當前是橫屏還是豎屏,而不直接使用感測器的話,沒什麼問題。如果像依靠重力感應的遊戲那樣直接使用感測器,就需要自己根據物理螢幕的座標系對感測器資料做轉化,否則就會出現座標系混亂的問題。

如果沒有沒有通過上面的d.getWidth()和d.getHeight()來檢測裝置的物理螢幕從確定哪個是landscape和porit模式,而是直接假設裝置是和手機一樣的模式。由於遊戲執行在landscape模式下,它們都把感測器資料右轉90度。這樣做法在手機上是沒有問題,但在平板電腦上是不應該轉化的,這是因為物理螢幕寬比高大的情況下,預設就是landscape模式。

現在回到原始碼,在這裡沒有區分手機和平板,僅僅是用來轉換加速度的方向而已,也沒有必要區分.

case Surface.ROTATION_0://手機處於正常狀態
                mSensorX = event.values[0];
                mSensorY = event.values[1];
                break;

這段就是如果手機的方向沒有旋轉,不管手機處於landscape還是porit模式,加速度的方向都不用變,而下面,如果手機旋轉了180度,說明x軸和y軸的方向完全反過來了,這時候對於加速度的方向就要調整到反向.還有90度和270度的情況都類似.

case Surface.ROTATION_180:
                mSensorX = -event.values[0];
                mSensorY = -event.values[1];
                break;