回転の処理

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

概要

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

このガイドでは、次のオリエンテーション トピックについて説明します。

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

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

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

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

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

  • 描画可能なリソース – 各方向に読み込まれる描画可能な描画可能オブジェクトを指定します。

レイアウト リソース

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

既定のプロジェクト テンプレート構造

このプロジェクトでは、Resources/layout フォルダーに 1 つの 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 ファイルが拡張されます。

横向きの同じ画面

Orientation-Specific レイアウト

レイアウト フォルダー (既定では portrait に設定され、 という名前のフォルダーを含めることによって 明示的に 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 読み込みを示します。

縦モードを印刷する縦と横のスクリーンショット

描画可能なリソース

ローテーション中、Android は描画可能なリソースをレイアウト リソースと同様に扱います。 この場合、システムは、それぞれ Resources/drawable フォルダーと 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 に含まれているとします。 レイアウト ファイルと同様に、デバイスを回転させると、次に示すように、指定した向きの描画可能な変更が行われます。

縦モードと横モードで表示されるMonkey.pngの異なるバージョン

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

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

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

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

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

たとえば、次のコードに示すように、 にRelativeLayout追加された 1 つの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処理に加えて、次のように を設定ConfigurationChangesActivityAttributeすることで、方向が変更されたときにアクティビティが再起動されないようにすることもできます。

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

デバイスがローテーションされると、アクティビティは再起動されません。 この場合、方向の変更を手動で処理するために、Activity では、次の 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 アクティビティが再作成されないため、パラメーターと共にそれ自体を保持します。 このコードでは、 をsurfaceOrientartionOnCreate引き続き 使用して、 の初期レイアウトを設定しますTextView。 その後、 OnConfigurationChanged 後続のすべてのレイアウト変更を処理します。

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

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

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

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

方向の変更中の状態の維持

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

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

まとめ

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