共用方式為


處理旋轉

本主題描述如何處理 Xamarin.Android 中的裝置方向變更。 它涵蓋如何使用 Android 資源系統自動載入特定裝置方向的資源,以及如何以程式設計方式處理方向變更。

概觀

由於行動裝置很容易旋轉,因此內建輪替是行動裝置OS的標準功能。 Android 提供複雜的架構來處理應用程式內的輪替,無論是以宣告方式在 XML 中建立使用者介面,還是以程序設計方式在程式代碼中建立。 在旋轉裝置上自動處理宣告式版面配置變更時,應用程式可以從與 Android 資源系統的緊密整合中獲益。 針對程式設計配置,必須手動處理變更。 這允許在運行時間進行更精細的控制,但犧牲開發人員的更多工作。 應用程式也可以退出退出活動重新啟動,並手動控制方向變更。

本指南會檢查下列方向主題:

  • 宣告式版面配置輪替 – 如何使用Android資源系統來建置方向感知應用程式,包括如何載入特定方向的配置和可繪製。

  • 程序設計版面配置旋轉 – 如何以程序設計方式新增控件,以及如何手動處理方向變更。

使用版面配置以宣告方式處理旋轉

藉由將檔案包含在遵循命名慣例的資料夾中,Android 會在方向變更時自動載入適當的檔案。 包括的支援如下:

  • 版面配置資源 – 指定每個方向的版面配置檔案擴充。

  • 可繪製的資源 – 指定要針對每個方向載入哪些可繪製專案。

版面配置資源

根據預設,包含在 Resources/layout 資料夾中的 Android XML (AXML) 檔案會用於轉譯活動的檢視。 如果未特別針對橫向提供額外的版面配置資源,則此資料夾的資源會用於直向和橫向。 請考慮預設項目範本所建立的項目結構:

默認專案範本結構

此專案會在 Resources/layout 資料夾中建立單一 Main.axml 檔案。 呼叫 Activity 的 OnCreate 方法時,它會擴大 Main.axml 中 定義的檢視, 它會宣告按鈕,如下列 XML 所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
<Button  
  android:id="@+id/myButton"
  android:layout_width="fill_parent" 
  android:layout_height="wrap_content" 
  android:text="@string/hello"/>
</LinearLayout>

如果裝置旋轉為橫向方向,則會再次呼叫 Activity 的 OnCreate 方法,並擴充相同的 Main.axml 檔案,如下列螢幕快照所示:

相同畫面,但橫向方向

方向特定版面配置

除了版面配置資料夾(預設為直向,也可以藉由包含名為的資料夾來明確命名layout-landlayout-port),應用程式可以在橫向中定義它所需的檢視,而不需要變更任何程式代碼。

假設 Main.axml 檔案包含下列 XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <TextView
    android:text="This is portrait"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent" />
</RelativeLayout>

如果名為 layout-land 的資料夾包含額外的 Main.axml 檔案新增至專案,在橫向時擴大版面配置,現在會導致 Android 載入新新增 的 Main.axml。 請考慮包含下列程式代碼的 Main.axml 檔案橫向版本(為了簡單起見,此 XML 類似於程式代碼的預設直向版本,但在 中使用 TextView不同的字串):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <TextView
    android:text="This is landscape"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent" />
</RelativeLayout>

執行此程式代碼並將裝置從直向旋轉到橫向會示範新的 XML 載入,如下所示:

直向和橫向螢幕快照列印直向模式

可繪製的資源

在輪替期間,Android 會處理類似配置資源的可繪製資源。 在此情況下,系統會分別從 Resources/drawableResources/drawable-land 資料夾取得可繪製專案。

例如,假設專案在 Resources/drawable 資料夾中包含名為 Monkey.png 的影像,其中會從 ImageView XML 中參考繪製,如下所示:

<ImageView
  android:layout_height="wrap_content"
  android:layout_width="wrap_content"
  android:src="@drawable/monkey"
  android:layout_centerVertical="true"
  android:layout_centerHorizontal="true" />

讓我們進一步假設資源/可繪製的土地中包含不同版本的 Monkey.png。 就像配置檔案一樣,當裝置旋轉時,指定方向的可繪製變更,如下所示:

以直向和橫向模式顯示的不同版本Monkey.png

以程式設計方式處理旋轉

有時候我們會在程式代碼中定義版面配置。 這可能會因為各種原因而發生,包括技術限制、開發人員喜好設定等。當我們以程式設計方式新增控件時,應用程式必須手動考慮裝置方向,當我們使用 XML 資源時,會自動處理此方向。

在程式代碼中新增控制件

若要以程式設計方式新增控制項,應用程式必須執行下列步驟:

  • 建立版面配置。
  • 設定版面配置參數。
  • 建立控件。
  • 設定控制項配置參數。
  • 將控件新增至版面配置。
  • 將版面配置設定為內容檢視。

例如,假設使用者介面是由新增至 RelativeLayout的單TextView一控件所組成,如下列程式代碼所示。

protected override void OnCreate (Bundle bundle)
{
  base.OnCreate (bundle);
                        
  // create a layout
  var rl = new RelativeLayout (this);

  // set layout parameters
  var layoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.FillParent);
  rl.LayoutParameters = layoutParams;
        
  // create TextView control
  var tv = new TextView (this);

  // set TextView's LayoutParameters
  tv.LayoutParameters = layoutParams;
  tv.Text = "Programmatic layout";

  // add TextView to the layout
  rl.AddView (tv);
        
  // set the layout as the content view
  SetContentView (rl);
}

此程式代碼會建立 類別的 RelativeLayout 實例,並設定其 LayoutParameters 屬性。 類別 LayoutParams 是Android封裝控件以可重複使用方式放置方式的方式。 建立版面配置實例之後,就可以建立控件並新增至該版面配置。 控制項也有 LayoutParameters,例如 TextView 此範例中的 。 TextView建立 之後,將它新增至 RelativeLayout ,並將 RelativeLayout 設定為 內容檢視會導致應用程式顯示 TextView ,如下所示:

直向模式和橫向模式中顯示的遞增計數器按鈕

偵測程式代碼中的方向

如果在呼叫 時 OnCreate ,應用程式嘗試針對每個方向載入不同的使用者介面(每次裝置旋轉時都會發生這種情況),它必須偵測方向,然後載入所需的使用者介面程序代碼。 Android 有一個名為 的 WindowManager類別,可用來透過 WindowManager.DefaultDisplay.Rotation 屬性判斷目前的裝置旋轉,如下所示:

protected override void OnCreate (Bundle bundle)
{
  base.OnCreate (bundle);
                        
  // create a layout
  var rl = new RelativeLayout (this);

  // set layout parameters
  var layoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.FillParent);
  rl.LayoutParameters = layoutParams;
                        
  // get the initial orientation
  var surfaceOrientation = WindowManager.DefaultDisplay.Rotation;
  // create layout based upon orientation
  RelativeLayout.LayoutParams tvLayoutParams;
                
  if (surfaceOrientation == SurfaceOrientation.Rotation0 || surfaceOrientation == SurfaceOrientation.Rotation180) {
    tvLayoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
  } else {
    tvLayoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
    tvLayoutParams.LeftMargin = 100;
    tvLayoutParams.TopMargin = 100;
  }
                        
  // create TextView control
  var tv = new TextView (this);
  tv.LayoutParameters = tvLayoutParams;
  tv.Text = "Programmatic layout";
        
  // add TextView to the layout
  rl.AddView (tv);
        
  // set the layout as the content view
  SetContentView (rl);
}

此程式代碼會將 TextView 設定為從畫面左上方 100 像素的位置,在旋轉至橫向時自動以動畫顯示到新的版面配置,如下所示:

檢視狀態會跨直向和橫向模式保留

防止活動重新啟動

除了處理 中OnCreate的所有內容之外,應用程式也可以藉由在 中ActivityAttribute設定 ConfigurationChanges ,防止活動在方向變更時重新啟動,如下所示:

[Activity (Label = "CodeLayoutActivity", ConfigurationChanges=Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize)]

現在當裝置旋轉時,活動不會重新啟動。 為了在此案例中手動處理方向變更,Activity 可以覆寫 OnConfigurationChanged 方法,並從傳入的物件判斷方向 Configuration ,如下列活動的新實作所示:

[Activity (Label = "CodeLayoutActivity", ConfigurationChanges=Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize)]
public class CodeLayoutActivity : Activity
{
  TextView _tv;
  RelativeLayout.LayoutParams _layoutParamsPortrait;
  RelativeLayout.LayoutParams _layoutParamsLandscape;
                
  protected override void OnCreate (Bundle bundle)
  {
    // create a layout
    // set layout parameters
    // get the initial orientation

    // create portrait and landscape layout for the TextView
    _layoutParamsPortrait = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
                
    _layoutParamsLandscape = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
    _layoutParamsLandscape.LeftMargin = 100;
    _layoutParamsLandscape.TopMargin = 100;
                        
    _tv = new TextView (this);
                        
    if (surfaceOrientation == SurfaceOrientation.Rotation0 || surfaceOrientation == SurfaceOrientation.Rotation180) {
      _tv.LayoutParameters = _layoutParamsPortrait;
    } else {
      _tv.LayoutParameters = _layoutParamsLandscape;
    }
                        
    _tv.Text = "Programmatic layout";
    rl.AddView (_tv);
    SetContentView (rl);
  }
                
  public override void OnConfigurationChanged (Android.Content.Res.Configuration newConfig)
  {
    base.OnConfigurationChanged (newConfig);
                        
    if (newConfig.Orientation == Android.Content.Res.Orientation.Portrait) {
      _tv.LayoutParameters = _layoutParamsPortrait;
      _tv.Text = "Changed to portrait";
    } else if (newConfig.Orientation == Android.Content.Res.Orientation.Landscape) {
      _tv.LayoutParameters = _layoutParamsLandscape;
      _tv.Text = "Changed to landscape";
    }
  }
}

在這裡,版 TextView's 面配置參數會針對橫向和直向初始化。 類別變數會保存參數以及 TextView 本身,因為當方向變更時,將不會重新建立活動。 程序代碼仍然使用 surfaceOrientartion 中的 OnCreate 來設定 的初始配置 TextView。 之後, OnConfigurationChanged 處理所有後續的版面配置變更。

當我們執行應用程式時,Android 會在裝置輪替時載入使用者介面變更,而不會重新啟動活動。

防止宣告式版面配置的活動重新啟動

如果我們在 XML 中定義版面配置,也可能防止裝置輪替所造成的活動重新啟動。 例如,如果我們想要防止活動重新啟動(基於效能考慮,或許如此),我們不需要針對不同的方向載入新資源,則可以使用此方法。

若要這樣做,我們會遵循與程序設計版面配置搭配使用的相同程式。 只要在 中ActivityAttribute設定ConfigurationChanges,就像我們稍早所做的CodeLayoutActivity一樣。 任何需要針對方向變更執行的程序代碼,都可以在方法中 OnConfigurationChanged 再次實作。

在方向變更期間維護狀態

無論是以宣告方式還是以程式設計方式處理旋轉,所有 Android 應用程式都應該實作相同的技術,以在裝置方向變更時管理狀態。 管理狀態很重要,因為系統會在輪替 Android 裝置時重新啟動執行中的活動。 Android 會這樣做,讓您輕鬆載入替代資源,例如專為特定方向設計的版面配置和可繪製專案。 重新啟動時,活動會遺失它可能儲存在本機類別變數中的任何暫時性狀態。 因此,如果活動與狀態相依,它必須在應用層級保存其狀態。 應用程式必須處理儲存和還原想要跨方向變更保留的任何應用程式狀態。

如需在Android中保存狀態的詳細資訊,請參閱 活動生命週期 指南。

摘要

本文涵蓋如何使用Android的內建功能來處理輪替。 首先,它說明如何使用Android資源系統來建立方向感知應用程式。 然後,它示範如何在程序代碼中新增控件,以及如何手動處理方向變更。