回転の処理

このトピックでは、Xamarin.Android でデバイスの向きの変更を処理する方法について説明します。 Android リソース システムを使用して、特定のデバイスの向きのリソースを自動的に読み込む方法と、向きの変更をプログラムで処理する方法について説明します。

概要

モバイル デバイスは簡単に回転できるため、組み込みの回転はモバイル OS の標準機能です。 Android は、ユーザー インターフェイスが XML で宣言的に作成されるか、コード内でプログラムによって作成されるかに関係なく、アプリケーション内のローテーションを処理するための高度なフレームワークを提供します。 回転したデバイスで宣言型レイアウトの変更を自動的に処理する場合、アプリケーションは Android リソース システムとの緊密な統合の恩恵を受けることができます。 プログラムによるレイアウトの場合は、変更を手動で処理する必要があります。 これにより、実行時により細かい制御が可能になりますが、開発者にとってより多くの作業が必要になります。 アプリケーションでは、アクティビティの再起動をオプトアウトし、方向の変更を手動で制御することもできます。

このガイドでは、次の向きのトピックについて説明します。

  • 宣言型レイアウトのローテーション – Android リソース システムを使用して、特定の向きのレイアウトと描画可能な両方のレイアウトを読み込む方法など、向きに対応したアプリケーションを構築する方法。

  • プログラムによるレイアウト回転 – プログラムによってコントロールを追加する方法と、方向の変更を手動で処理する方法。

レイアウトを使用して宣言的に回転を処理する

名前付け規則に従うフォルダーにファイルを含めることで、Android は向きが変更されたときに適切なファイルを自動的に読み込みます。 これには、次のサポートが含まれます。

  • レイアウト リソース – 向きごとに拡張するレイアウト ファイルを指定します。

  • 描画可能リソース – 向きごとに読み込まれる描画可能なリソースを指定します。

レイアウト リソース

既定では、 Resources/layout フォルダーに含まれる Android XML (AXML) ファイルは、アクティビティのビューのレンダリングに使用されます。 このフォルダーのリソースは、横向き専用に追加のレイアウト リソースが提供されていない場合に、縦向きと横向きの両方に使用されます。 既定のプロジェクト テンプレートによって作成されるプロジェクト構造を考えてみましょう。

Default project template structure

このプロジェクトは、Resources/layout フォルダーに 1 つの Main.axml ファイルを作成します。 アクティビティの 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>

デバイスが横向きに回転した場合、次のスクリーンショットに示すように、アクティビティの OnCreate メソッドが再度呼び出され、同じ Main.axml ファイルが拡張されます。

Same screen but in landscape orientation

Orientation-Specific レイアウト

レイアウト フォルダー (既定では縦向きに設定され、名前付きフォルダーを含めることで 明示的に layout-port という名前 layout-landを付けることもできます) に加えて、アプリケーションでは、コードを変更せずに横向きの場合に必要なビューを定義できます。

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>

追加の Main.axml ファイルを含む layout-land という名前のフォルダーがプロジェクトに追加された場合、横向きのレイアウトを拡張すると、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 読み込みが示されます。

Portrait and landscape screenshots printing the portrait mode

Drawable リソース

ローテーション中、Android は描画可能なリソースをレイアウト リソースと同様に扱います。 この場合、システムは、それぞれ リソース/描画可能フォルダーと Resources/drawable-land フォルダーから 描画可能な アイテムを取得します。

たとえば、プロジェクトに resources/drawable フォルダーに Monkey.png という名前のイメージが含まれているとします。ここで、drawable は次のような XML 内から ImageView 参照されます。

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

さらに、別のバージョンの Monkey.pngResources/drawable-land に含まれているとします。 レイアウト ファイルの場合と同様に、デバイスが回転すると、次に示すように、指定した向きの描画可能な変更が行われます。

Different version of Monkey.png shown in portrait and landscape modes

プログラムによる回転の処理

場合によっては、コードでレイアウトを定義します。 これは、技術的な制限、開発者の好みなど、さまざまな理由で発生する可能性があります。プログラムによってコントロールを追加する場合、アプリケーションはデバイスの向きを手動で考慮する必要があります。これは、XML リソースを使用するときに自動的に処理されます。

コードでのコントロールの追加

プログラムでコントロールを追加するには、アプリケーションで次の手順を実行する必要があります。

  • レイアウトを作成します。
  • レイアウト パラメーターを設定します。
  • コントロールを作成します。
  • コントロール レイアウト パラメーターを設定します。
  • レイアウトにコントロールを追加します。
  • レイアウトをコンテンツ ビューとして設定します。

たとえば、次のコードに示すように、a に追加された RelativeLayout1 つの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 表示されます。

Increment counter button shown in both portrait and landscape modes

コードでの方向の検出

アプリケーションが呼び出されたときに OnCreate 向きごとに異なるユーザー インターフェイスを読み込もうとする場合 (これはデバイスが回転するたびに発生します)、向きを検出し、目的のユーザー インターフェイス コードを読み込む必要があります。 Android には、次に示すように、プロパティを介してWindowManager.DefaultDisplay.Rotation現在のデバイスの回転を決定するために使用できる 、というクラスWindowManagerがあります。

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);
}

このコードは、次に示すように、画面の左上から 100 ピクセルの位置に設定 TextView します。横向きに回転すると、新しいレイアウトに自動的にアニメーション化されます。

View state is preserved across portrait and landscape modes

アクティビティの再起動の防止

アプリケーションではOnCreate、すべての処理に加えて、次のように設定ConfigurationChangesActivityAttributeすることで、方向が変更されたときにアクティビティが再起動されないようにすることもできます。

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

デバイスがローテーションされると、アクティビティは再起動されません。 この場合、方向の変更を手動で処理するために、Activity はメソッドを OnConfigurationChanged オーバーライドし、以下の Activity の新しい実装のように渡されるオブジェクトからの 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 共にパラメーターを保持します。 コードでは引き続き in OnCreatesurfaceOrientartion使用して、TextView. その後、 OnConfigurationChanged 後続のすべてのレイアウト変更を処理します。

アプリケーションを実行すると、Android はデバイスのローテーションが発生したときにユーザー インターフェイスの変更を読み込み、アクティビティを再起動しません。

宣言型レイアウトのアクティビティの再起動を防止する

XML でレイアウトを定義すると、デバイスのローテーションによって発生するアクティビティの再起動も防止できます。 たとえば、アクティビティの再起動を (パフォーマンス上の理由から) 防ぐ必要があり、さまざまな向きに新しいリソースを読み込む必要がない場合は、このアプローチを使用できます。

これを行うには、プログラムレイアウトで使用するのと同じ手順に従います。 前にActivityAttribute行ったように、単純に設定ConfigurationChangesしますCodeLayoutActivity。 方向の変更のために実行する必要があるコードは、メソッドに OnConfigurationChanged 再度実装できます。

オリエンテーションの変更中の状態の維持

回転を宣言的に処理するかプログラムで処理するかに関係なく、すべての Android アプリケーションは、デバイスの向きが変化したときに状態を管理するための同じ手法を実装する必要があります。 Android デバイスのローテーション時に実行中のアクティビティが再起動されるため、状態の管理は重要です。 Android では、特定の向き専用に設計されたレイアウトや描画可能なリソースなど、代替リソースを簡単に読み込めるようにします。 再起動すると、アクティビティはローカル クラス変数に格納されている可能性がある一時的な状態を失います。 したがって、アクティビティが状態に依存している場合は、その状態をアプリケーション レベルで保持する必要があります。 アプリケーションは、向きの変更の間で保持するアプリケーション状態の保存と復元を処理する必要があります。

Android での状態の永続化の詳細については、 アクティビティ ライフサイクル ガイドを参照してください。

まとめ

この記事では、Android の組み込み機能を使用してローテーションを操作する方法について説明しました。 まず、Android リソース システムを使用して向きに対応するアプリケーションを作成する方法について説明しました。 次に、コードにコントロールを追加する方法と、方向の変更を手動で処理する方法について説明しました。