Android 橫豎屏處理的知識小結

weixin_34253539發表於2019-03-06

Android 手機一般都支援橫豎屏旋轉,系統也會提供一個設定,控制允不允許旋轉。這裡對如何在 App 中控制介面的旋轉方向做一個小結。

介面旋轉方向的決定因素

決定一個介面顯示為橫屏/豎屏的因素有幾個:

  • 系統的設定項,一般可以設定為只允許豎屏或可旋轉切換。
  • 裝置的物理感測器感應到的裝置方向。
  • 不同 App 裡的程式碼對橫豎屏的設定。

需要注意的一點是,這三個因素沒有固定的優先順序。所以即使系統的設定項中設定了固定為豎屏,App 裡的程式碼也可以將介面設定為橫屏,當然影響的範圍僅限於 App 內部介面。

程式碼中設定橫豎屏的方法

我們有2種方法設定橫豎屏:

  1. AndroidManifest.xml 檔案中,對 <activity> 標籤設定android:screenOrientation 屬性,具體的值和作用見下文。
  2. 在 Java 中呼叫 Activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE),具體的值和作用見下文。

如果在 xml 中設定,則在初始化介面時就已經確定好其方向。而在如果 Java 中設定,則可以做到根據不同情況動態設定。

設定橫豎屏時傳入的值和作用

以下列出在 AndroidManifest.xml<activity> 標籤的 android:screenOrientation 屬性的可能的值。如果採用 Java 方式設定,引數與之一一對應。(參考 官方文件)

  • unspecified:預設值,不做任何指定,由系統來決定顯示方向。這將由上一個 activity 來決定,或者當 activity 處於棧底時由使用者設定的方向來決定。
  • landscape: 保持橫屏。
  • portrait: 保持豎屏。
  • user: 使用者當前設定的 orientation 值。
  • behide: 保持和上一個 Activity 的方向一致。
  • sensor: 完全根據物理感測器的方向來決定。注意用這個值時會忽略使用者在系統設定中的旋轉開關狀態。(注意一般機器即使用這個值也不會支援豎屏旋轉180度)
  • nosensor: 忽略物理感測器的方向。這將導致使用者旋轉手機時不會切換橫豎屏。
  • sensorLandscape: 保持橫屏,但可以根據物理感測器來決定橫屏的方向。
  • sensorPortrait: 保持豎屏,但可以根據物理感測器來決定豎屏的方向。
  • reverseLandscape: 保持橫屏,但方向與使用 landscape 時相反。
  • reversePortrait: 保持豎屏,但方向與使用 portrait 時相反。
  • fullSensor: 與 sensor 大致相同,區別在於這個屬性會允許4個方向都可以旋轉。
  • userLandscape: 保持橫屏,但可以在使用者允許旋轉的情況下,根據物理感測器來決定橫屏的方向。(注意與 sensorLandscape 對比)
  • userPortrait: 保持豎屏,但可以在使用者允許旋轉的情況下,根據物理感測器來決定豎屏的方向。(注意與 sensorPortrait 對比)
  • fullUser: 與 user 大致相同,區別在於如果使用者允許旋轉,這個屬性允許4個方向都可以旋轉。
  • locked: 螢幕方向會鎖定在當前方向,不能再旋轉。

該屬性的值有很多,但一般比較常用的幾個值也就幾個:unspecified(允許旋轉)、landscape(固定為橫屏)、portrait(固定為豎屏)。而且一般我們只需要在 AndroidManifest.xml 中指定好固定的值就可以了。除非你對橫豎屏切換有自己的一套邏輯,才需要在不同的邏輯下進行不同的設定。

橫豎屏切換時引起的 Activity 重新建立

預設情況下,橫豎屏切換會引起當前 Activity 銷燬然後重新建立。所以我們可以在 Activity 重新建立時根據當前橫豎屏狀態做一些差異化。最常見的就是根據橫豎屏採用不同的 layout xml。

  • res 目錄下建立 layout-landlayout-port 目錄,在不同目錄下分別放一個同名的 layout xml 檔案,然後在程式碼里正常使用這個 layout 檔案,系統就會在橫豎屏切換並重新建立 Activity 時自動使用對應的 layout 檔案。如:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_layout);
    }
    
  • 如果不想通過建立多個 layout 檔案來區分橫豎屏佈局,也可以用 Java 程式碼來做一些差異化,只要使用以下判斷即可獲取當前橫豎屏的資訊。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_layout);
        // 獲取橫豎屏資訊
        int orientation = getResources().getConfiguration().orientation;
        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            // 針對橫屏做一些處理
        } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            // 針對豎屏做一些處理
        }
    }
    

有一個點需要注意,關於 Activity 的生命週期被重新呼叫,有的資料說:

當不設定 Activity 的 android:configChanges 屬性時,切到橫屏時會重新執行一次生命週期,而切到豎屏時會執行兩次。
如果設定了 android:configChanges="orientation" 屬性,則都只會執行一次。

但是個人實驗沒有驗證出這個說法。保險起見,可以加上 android:configChanges="orientation" 屬性,確保生命週期只呼叫一次。

阻止橫豎屏切換時 Activity 重啟

雖然我們可以用上面的方法對橫豎屏做不同的佈局,但是橫豎屏切換導致的 Activity 重新建立還會引起使用者當前狀態、資料的丟失。例如 EditText 中輸入到一半的文字在 Activity 重新建立後會丟失。所以如果有辦法能在旋轉螢幕時不重新建立 Activity,應該是更好的一種選擇。
我們可以在 AndroidManifest.xml 中為相應的 <activity> 設定android:configChanges 屬性,讓它不在橫豎屏切換時引起 Activity 的重新建立。
Android 3.2(API Level 13)以前的 SDK 可以使用如下配置
android:configChanges="orientation|keyboardHidden"
而 Android 3.2 以後的 SDK 則需要設定為
android:configChanges="keyboardHidden|orientation|screenSize"
這樣一來,橫豎屏切換時 Activity 將不會重新建立。

橫豎屏切換時的回撥

一旦我們按照上述方法設定了 android:configChanges 屬性,那麼橫豎屏切換時 Activity 將不會重新建立,那麼當螢幕旋轉時,我們在程式碼上怎麼感知呢?可以通過 Override ActivityViewonConfigurationChanged 方法。

@Override  
public void onConfigurationChanged(Configuration newConfig) {  
    super.onConfigurationChanged(newConfig);
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {  
        // 針對橫屏做一些處理
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){  
        // 針對豎屏做一些處理
    }  
}

判斷當前螢幕方向

有了上面的橫豎屏切換的回撥,我們就可以及時感知到螢幕的狀態變化。但因為豎屏、橫屏都有兩個不同的方向,所以 Android 系統又提供了方法給我們獲取當前螢幕精確的旋轉方向。

Display display = activity.getWindowManager().getDefaultDisplay();
int rotation = display.getRotation();
//
// rotation 的值表示了當前螢幕的旋轉角度,可能的值有:
// Surface.ROTATION_0、Surface.ROTATION_90、
// Surface.ROTATION_180、Surface.ROTATION_270。
//

這樣我們就可以判斷當前螢幕的方向了。

舉個例子,以下方法獲取當前螢幕的旋轉方向,然後將旋轉方向鎖定,使使用者不能再旋轉。

public static void lockScreenOrientation(Activity activity) {
    Display display = activity.getWindowManager().getDefaultDisplay();
    switch (display.getRotation()) {
        case Surface.ROTATION_90:
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            break;
        case Surface.ROTATION_180:
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
            break;
        case Surface.ROTATION_270:
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
            break;
        default:
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }
}

總結

  1. android:screenOrientation 屬性可以控制 Activity 的方向,常用的值有 unspecified(預設,可旋轉)、landscape(保持橫屏)、portrait(保持豎屏)。
  2. 觸發橫豎屏切換時,如果希望 Activity 銷燬並重新建立,則可以設定 Activity 的 android:configChanges="orientation"
  3. 觸發橫豎屏切換時,如果不希望 Activity 銷燬並重新建立,可以設定 Activity 的 android:configChanges="keyboardHidden|orientation|screenSize"。此後橫豎屏切換時,可以在 Activity.onConfigurationChangedView.onConfigurationChanged 方法中處理切換後的事情。
  4. 在任何時候可以用 getResources().getConfiguration().orientation 獲取螢幕的橫豎屏狀態。
  5. 在任何時候可以用 activity.getWindowManager().getDefaultDisplay().getRotation() 獲取螢幕的旋轉角度。

相關文章