デバイスの向き
アプリケーションの使用方法と、ユーザー エクスペリエンスを向上させるために横向きを組み込む方法を検討することが重要です。 個々のレイアウトは、複数の向きに対応し、使用可能な領域を最大限活用するように設計できます。 アプリケーション レベルでは、ローテーションを無効または有効にすることができます。
向きの制御
Xamarin.Forms を使用する場合、サポートされているデバイスの向きの制御方法は、個々のプロジェクトの設定を使用することです。
iOS
iOS では、デバイスの向きは Info.plist ファイルを使用してアプリケーション用に構成されます。 このドキュメントの上部にある IDE オプションを使用して、見たい手順を選択します。
Visual Studio で iOS プロジェクトを開き、Info.plist を開きます。 このファイルを開くと、[iPhone デプロイ情報] タブで始まる構成パネルが表示されます。
Android
Android で向きを制御するには、MainActivity.cs を開き、MainActivity
クラスを装飾する属性を使用して向きを設定します。
namespace MyRotatingApp.Droid
{
[Activity (Label = "MyRotatingApp.Droid", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, ScreenOrientation = ScreenOrientation.Landscape)] //This is what controls orientation
public class MainActivity : FormsAppCompatActivity
{
protected override void OnCreate (Bundle bundle)
...
Xamarin.Android では、向きを指定するためのいくつかのオプションがサポートされています。
- 横向き – センサー データに関係なく、アプリケーションの向きを強制的に横向きにします。
- 縦向き – センサー データに関係なく、アプリケーションの向きを強制的に縦向きにします。
- ユーザー – ユーザーの好みの向きを使用してアプリケーションが表示されるようにします。
- 背後 – アプリケーションの向きが、その背後にあるアクティビティの向きと同じになります。
- センサー – ユーザーが自動回転を無効にした場合でも、アプリケーションの向きがセンサーによって決定されます。
- SensorLandscape – センサー データを使用して画面の向きを変更している間、アプリケーションで横向きを使用します (画面が上下逆さまに表示されないようにします)。
- SensorPortrait – センサー データを使用して画面の向きを変更している間、アプリケーションで縦向きを使用します (画面が上下逆さまに表示されないようにします)。
- ReverseLandscape – アプリケーションで横向きを使用し、通常とは反対の向きになるため、"逆さま" に表示されます。
- ReversePortrait – アプリケーションで縦向きを使用し、通常とは反対の向きになるため、"逆さま" に表示されます。
- FullSensor – アプリケーションはセンサー データに依存して (可能な 4 方向のうち) 正しい向きを選択します。
- FullUser – アプリケーションでユーザーが設定した向きが使用されます。 自動回転が有効になっている場合は、4 つの向きすべてを使用できます。
- UserLandscape – [サポートされていません] では、ユーザーが自動回転を有効にしていない限り、アプリケーションは横向きを使用します。その場合は、センサーを使用して向きが決定されます。 このオプションではコンパイルが中断されます。
- UserPortrait – [サポートされていません] では、ユーザーが自動回転を有効にしていない限り、アプリケーションは縦向きを使用します。その場合は、センサーを使用して向きが決定されます。 このオプションではコンパイルが中断されます。
- ロック – [サポートされていません] では、デバイスの物理的な向きの変更に反応せずに、アプリケーションは起動時の画面の向きを使用します。 このオプションではコンパイルが中断されます。
Android のネイティブ API では、ユーザーが明示的に表現した好みに矛盾するオプションなど、向きの管理方法を多く制御できることに注意してください。
ユニバーサル Windows プラットフォーム
ユニバーサル Windows プラットフォーム (UWP) では、Package.appxmanifest ファイルでサポートされている向きが設定されます。 マニフェストを開くと、サポートされている向きを選択できる構成パネルが表示されます。
向きの変化への対応
Xamarin.Forms では、共有コードの向きの変更をアプリに通知するためのネイティブ イベントは提供されません。 しかし、Xamarin.Essentials には [DeviceDisplay
] クラスが含まれており、向きの変更が通知されます。
Xamarin.Essentials を使用せずに向きを検出するには、Page
の幅または高さが変化した場合に発生する Page
の SizeChanged
イベントを監視します。 Page
の幅が高さより大きい場合、デバイスは横向きモードになります。 詳細については、「画面の向きに基づいてイメージを表示する」を参照してください。
または、OnSizeAllocated
メソッドを Page
でオーバーライドして、そこにレイアウト変更ロジックを挿入することもできます。 この OnSizeAllocated
メソッドは、デバイスが回転するたびに発生する新しいサイズが Page
に割り当てられるたびに呼び出されます。 OnSizeAllocated
の基本実装では重要なレイアウト関数が実行されるため、オーバーライドで基本実装を呼び出すことが重要であることに注意してください。
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height); //must be called
}
この手順を実行しないと、ページが機能しなくなります。
この OnSizeAllocated
メソッドは、デバイスの回転時に何度も呼び出される可能性があることに注意してください。 毎回レイアウトを変更するのはリソースの無駄遣いで、ちらつきの原因にもなります。 ページ内のインスタンス変数を使用して、横向きか縦向きかを追跡し、変更がある場合にのみ再描画することを検討してください。
private double width = 0;
private double height = 0;
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height); //must be called
if (this.width != width || this.height != height)
{
this.width = width;
this.height = height;
//reconfigure layout
}
}
デバイスの向きの変更が検出されたら、使用可能な領域の変化に対応するために、ユーザー インターフェイスにビューを追加または削除できます。 たとえば、各プラットフォームに組み込まれた縦向きの電卓について考えてみましょう。
横向きの場合は次のとおりです。
アプリでは、横向きにより多くの機能を追加することで、使用可能な領域を活用していることに注意してください。
応答性の高いレイアウト
組み込みのレイアウトを使用して、デバイスが回転したときに適切に遷移するようにインターフェイスを設計することができます。 向きの変化に対応しても引き続き魅力的なインターフェイスを設計する場合は、次の一般的なルールを考慮してください。
- 比率に注意を払う - 比率に関して特定の仮定が行われた場合に、向きの変化に関する問題が生じる可能性があります。 たとえば、縦向きの画面の縦 1/3 の領域に十分な領域があるビューは、横向きの画面の縦 1/3 の領域に収まらない場合があります。
- 絶対値には注意してください – 縦向きの場合に意味のある絶対値 (ピクセル) は、横向きでは意味がない場合があります。 絶対値が必要な場合は、入れ子になったレイアウトを使用して、その影響を特定します。 たとえば、項目テンプレートの高さが保証されている場合は、
TableView
ItemTemplate
で絶対値を使用するのが妥当です。
上記の規則は、複数の画面サイズに対応するインターフェイスを実装する場合にも適用され、一般にベスト プラクティスと考えられています。 このガイドの残りの部分では、Xamarin.Forms の主要な各レイアウトを使用した応答性に優れたレイアウトの具体的な例について説明します。
Note
わかりやすくするために、次のセクションでは、一度に Layout
の 1 種類のみを使用して応答性に優れた レイアウトを実装する方法を示します。 実際には、Layout
を混ぜて、各コンポーネントに対してより単純な、または最も直感的な Layout
を使用して、目的のレイアウトを実現する方が単純である場合が多くあります。
StackLayout
縦向きに表示される次のアプリケーションについて考えてみましょう。
横向きの場合は次のとおりです。
これは、次の XAML で実現されます。
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResponsiveLayout.StackLayoutPageXaml"
Title="Stack Photo Editor - XAML">
<ContentPage.Content>
<StackLayout Spacing="10" Padding="5" Orientation="Vertical"
x:Name="outerStack"> <!-- can change orientation to make responsive -->
<ScrollView>
<StackLayout Spacing="5" HorizontalOptions="FillAndExpand"
WidthRequest="1000">
<StackLayout Orientation="Horizontal">
<Label Text="Name: " WidthRequest="75"
HorizontalOptions="Start" />
<Entry Text="deer.jpg"
HorizontalOptions="FillAndExpand" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Date: " WidthRequest="75"
HorizontalOptions="Start" />
<Entry Text="07/05/2015"
HorizontalOptions="FillAndExpand" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Tags:" WidthRequest="75"
HorizontalOptions="Start" />
<Entry Text="deer, tiger"
HorizontalOptions="FillAndExpand" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Button Text="Save" HorizontalOptions="FillAndExpand" />
</StackLayout>
</StackLayout>
</ScrollView>
<Image Source="deer.jpg" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
一部の C# は、デバイスの向きに基づいて outerStack
の向きを変更するために使用されます。
protected override void OnSizeAllocated (double width, double height){
base.OnSizeAllocated (width, height);
if (width != this.width || height != this.height) {
this.width = width;
this.height = height;
if (width > height) {
outerStack.Orientation = StackOrientation.Horizontal;
} else {
outerStack.Orientation = StackOrientation.Vertical;
}
}
}
次の点に注意してください。
outerStack
は、使用可能な領域を最大限に活用するために、向きによって画像とコントロールが水平スタックまたは垂直スタックとして表示されるように調整されます。
AbsoluteLayout
縦向きに表示される次のアプリケーションについて考えてみましょう。
横向きの場合は次のとおりです。
これは、次の XAML で実現されます。
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResponsiveLayout.AbsoluteLayoutPageXaml"
Title="AbsoluteLayout - XAML" BackgroundImageSource="deer.jpg">
<ContentPage.Content>
<AbsoluteLayout>
<ScrollView AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="PositionProportional,SizeProportional">
<AbsoluteLayout>
<Image Source="deer.jpg"
AbsoluteLayout.LayoutBounds=".5,0,300,300"
AbsoluteLayout.LayoutFlags="PositionProportional" />
<BoxView Color="#CC1A7019" AbsoluteLayout.LayoutBounds=".5
300,.7,50" AbsoluteLayout.LayoutFlags="XProportional
WidthProportional" />
<Label Text="deer.jpg" AbsoluteLayout.LayoutBounds = ".5
310,1, 50" AbsoluteLayout.LayoutFlags="XProportional
WidthProportional" HorizontalTextAlignment="Center" TextColor="White" />
</AbsoluteLayout>
</ScrollView>
<Button Text="Previous" AbsoluteLayout.LayoutBounds="0,1,.5,60"
AbsoluteLayout.LayoutFlags="PositionProportional
WidthProportional"
BackgroundColor="White" TextColor="Green" BorderRadius="0" />
<Button Text="Next" AbsoluteLayout.LayoutBounds="1,1,.5,60"
AbsoluteLayout.LayoutFlags="PositionProportional
WidthProportional" BackgroundColor="White"
TextColor="Green" BorderRadius="0" />
</AbsoluteLayout>
</ContentPage.Content>
</ContentPage>
次の点に注意してください。
- ページがレイアウトされているため、応答性を導入するための手続き型コードは必要ありません。
ScrollView
は、画面の高さがボタンと画像の固定された高さの合計より小さい場合でも、ラベルを表示できるようにするために使用されています。
RelativeLayout
縦向きに表示される次のアプリケーションについて考えてみましょう。
横向きの場合は次のとおりです。
これは、次の XAML で実現されます。
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResponsiveLayout.RelativeLayoutPageXaml"
Title="RelativeLayout - XAML"
BackgroundImageSource="deer.jpg">
<ContentPage.Content>
<RelativeLayout x:Name="outerLayout">
<BoxView BackgroundColor="#AA1A7019"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=1}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=1}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=0,Constant=0}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=0,Constant=0}" />
<ScrollView
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=1}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=1,Constant=-60}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=0,Constant=0}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=0,Constant=0}">
<RelativeLayout>
<Image Source="deer.jpg" x:Name="imageDeer"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=.8}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=.1}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=0,Constant=10}" />
<Label Text="deer.jpg" HorizontalTextAlignment="Center"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=1}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=0,Constant=75}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=0,Constant=0}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToView,ElementName=imageDeer,Property=Height,Factor=1,Constant=20}" />
</RelativeLayout>
</ScrollView>
<Button Text="Previous" BackgroundColor="White" TextColor="Green" BorderRadius="0"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=1,Constant=-60}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=0,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=0,Constant=60}"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=.5}"
/>
<Button Text="Next" BackgroundColor="White" TextColor="Green" BorderRadius="0"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=.5}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=1,Constant=-60}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=0,Constant=60}"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=.5}"
/>
</RelativeLayout>
</ContentPage.Content>
</ContentPage>
次の点に注意してください。
- ページがレイアウトされているため、応答性を導入するための手続き型コードは必要ありません。
ScrollView
は、画面の高さがボタンと画像の固定された高さの合計より小さい場合でも、ラベルを表示できるようにするために使用されています。
グリッド
縦向きに表示される次のアプリケーションについて考えてみましょう。
横向きの場合は次のとおりです。
これは、次の XAML で実現されます。
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResponsiveLayout.GridPageXaml"
Title="Grid - XAML">
<ContentPage.Content>
<Grid x:Name="outerGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="60" />
</Grid.RowDefinitions>
<Grid x:Name="innerGrid" Grid.Row="0" Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="deer.jpg" Grid.Row="0" Grid.Column="0" HeightRequest="300" WidthRequest="300" />
<Grid x:Name="controlsGrid" Grid.Row="0" Grid.Column="1" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="Name:" Grid.Row="0" Grid.Column="0" />
<Label Text="Date:" Grid.Row="1" Grid.Column="0" />
<Label Text="Tags:" Grid.Row="2" Grid.Column="0" />
<Entry Grid.Row="0" Grid.Column="1" />
<Entry Grid.Row="1" Grid.Column="1" />
<Entry Grid.Row="2" Grid.Column="1" />
</Grid>
</Grid>
<Grid x:Name="buttonsGrid" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Previous" Grid.Column="0" />
<Button Text="Save" Grid.Column="1" />
<Button Text="Next" Grid.Column="2" />
</Grid>
</Grid>
</ContentPage.Content>
</ContentPage>
次の手続き型コードに沿って、回転の変更を処理します。
private double width;
private double height;
protected override void OnSizeAllocated (double width, double height){
base.OnSizeAllocated (width, height);
if (width != this.width || height != this.height) {
this.width = width;
this.height = height;
if (width > height) {
innerGrid.RowDefinitions.Clear();
innerGrid.ColumnDefinitions.Clear ();
innerGrid.RowDefinitions.Add (new RowDefinition{ Height = new GridLength (1, GridUnitType.Star) });
innerGrid.ColumnDefinitions.Add (new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) });
innerGrid.ColumnDefinitions.Add (new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) });
innerGrid.Children.Remove (controlsGrid);
innerGrid.Children.Add (controlsGrid, 1, 0);
} else {
innerGrid.RowDefinitions.Clear();
innerGrid.ColumnDefinitions.Clear ();
innerGrid.ColumnDefinitions.Add (new ColumnDefinition{ Width = new GridLength (1, GridUnitType.Star) });
innerGrid.RowDefinitions.Add (new RowDefinition { Height = new GridLength (1, GridUnitType.Auto) });
innerGrid.RowDefinitions.Add (new RowDefinition { Height = new GridLength (1, GridUnitType.Star) });
innerGrid.Children.Remove (controlsGrid);
innerGrid.Children.Add (controlsGrid, 0, 1);
}
}
}
次の点に注意してください。
- ページがレイアウトされているため、制御のグリッド配置を変更する方法があります。