Orientation de l’appareil
Il est important de prendre en compte la façon dont votre application sera utilisée et comment l’orientation paysage peut être incorporée pour améliorer l’expérience utilisateur. Les dispositions individuelles peuvent être conçues pour prendre en charge plusieurs orientations et utiliser le mieux l’espace disponible. Au niveau de l’application, la rotation peut être désactivée ou activée.
Contrôle de l’orientation
Lors de l’utilisation Xamarin.Forms, la méthode prise en charge pour contrôler l’orientation de l’appareil consiste à utiliser les paramètres de chaque projet individuel.
iOS
Sur iOS, l’orientation de l’appareil est configurée pour les applications à l’aide du fichier Info.plist . Utilisez les options IDE en haut de ce document pour sélectionner les instructions que vous souhaitez voir :
Dans Visual Studio, ouvrez le projet iOS et ouvrez Info.plist. Le fichier s’ouvre dans un panneau de configuration, en commençant par l’onglet Informations de déploiement iPhone :
Android
Pour contrôler l’orientation sur Android, ouvrez MainActivity.cs et définissez l’orientation à l’aide de l’attribut décorant la MainActivity
classe :
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 prend en charge plusieurs options pour spécifier l’orientation :
- Paysage : force l’orientation de l’application à être paysage , quelles que soient les données du capteur.
- Portrait : force l’orientation de l’application à être portrait, quelles que soient les données du capteur.
- Utilisateur : provoque la présentation de l’application à l’aide de l’orientation préférée de l’utilisateur.
- Derrière : l’orientation de l’application est la même que l’orientation de l’activité derrière elle.
- Capteur : entraîne la détermination de l’orientation de l’application par le capteur, même si l’utilisateur a désactivé la rotation automatique.
- SensorLandscape : provoque l’utilisation de l’orientation paysage par l’application lors de l’utilisation des données du capteur pour modifier la direction de l’écran (de sorte que l’écran n’est pas considéré comme à l’envers).
- SensorPortrait : provoque l’utilisation de l’orientation portrait par l’application lors de l’utilisation de données de capteur pour modifier la direction de l’écran (de sorte que l’écran n’est pas considéré comme à l’envers).
- ReverseLandscape : provoque l’utilisation de l’orientation paysage par l’application, face à la direction opposée de l’habitude, afin d’apparaître « à l’envers ».
- ReversePortrait : provoque l’utilisation de l’orientation portrait par l’application, face à la direction opposée de l’habitude, afin d’apparaître « à l’envers ».
- FullSensor : fait en sorte que l’application s’appuie sur les données du capteur pour sélectionner l’orientation correcte (sur les 4 possibles).
- FullUser : provoque l’utilisation des préférences d’orientation de l’utilisateur. Si la rotation automatique est activée, toutes les 4 orientations peuvent être utilisées.
- UserLandscape : [Non pris en charge] oblige l’application à utiliser l’orientation paysage, sauf si l’utilisateur a activé la rotation automatique, auquel cas il utilisera le capteur pour déterminer l’orientation. Cette option interrompt la compilation.
- UserPortrait : [Non pris en charge] entraîne l’utilisation de l’orientation portrait par l’application, sauf si l’utilisateur a activé la rotation automatique, auquel cas il utilisera le capteur pour déterminer l’orientation. Cette option interrompt la compilation.
- Verrouillé : [Non pris en charge] entraîne l’utilisation de l’orientation de l’écran, quel qu’il soit au lancement, sans répondre aux modifications apportées à l’orientation physique de l’appareil. Cette option interrompt la compilation.
Notez que les API Android natives offrent beaucoup de contrôle sur la façon dont l’orientation est gérée, y compris les options qui contredisent explicitement les préférences exprimées par l’utilisateur.
Plateforme Windows universelle
Sur le plateforme Windows universelle (UWP), les orientations prises en charge sont définies dans le fichier Package.appxmanifest. L’ouverture du manifeste affiche un panneau de configuration dans lequel les orientations prises en charge peuvent être sélectionnées.
Réaction aux modifications apportées à l’orientation
Xamarin.Forms n’offre aucun événement natif pour informer votre application des modifications d’orientation dans le code partagé. Toutefois,Xamarin.Essentials contient une classe [DeviceDisplay
] qui fournit des notifications de modifications d’orientation.
Pour détecter les orientations sans Xamarin.Essentials, surveillez l’événement SizeChanged
du Page
, qui se déclenche lorsque la largeur ou la hauteur des Page
modifications est déclenchée. Lorsque la largeur du paramètre Page
est supérieure à la hauteur, l’appareil est en mode paysage. Pour plus d’informations, consultez Afficher une image basée sur l’orientation de l’écran.
Vous pouvez également remplacer la OnSizeAllocated
méthode sur une Page
logique de modification de disposition. La OnSizeAllocated
méthode est appelée chaque fois qu’une Page
nouvelle taille est allouée, ce qui se produit chaque fois que l’appareil est pivoté. Notez que l’implémentation de base d’effectuer des fonctions de OnSizeAllocated
disposition importantes, il est donc important d’appeler l’implémentation de base dans le remplacement :
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height); //must be called
}
L’échec de cette étape entraîne une page non fonctionnelle.
Notez que la OnSizeAllocated
méthode peut être appelée plusieurs fois lorsqu’un appareil est pivoté. La modification de votre disposition à chaque fois gaspille des ressources et peut entraîner un scintillement. Envisagez d’utiliser une variable d’instance dans votre page pour suivre si l’orientation est en paysage ou portrait, et redessiner uniquement en cas de modification :
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
}
}
Une fois qu’une modification de l’orientation de l’appareil a été détectée, vous pouvez ajouter ou supprimer des vues supplémentaires à/de votre interface utilisateur pour réagir au changement dans l’espace disponible. Par exemple, considérez la calculatrice intégrée sur chaque plateforme en portrait :
et paysage :
Notez que les applications tirent parti de l’espace disponible en ajoutant davantage de fonctionnalités dans le paysage.
Disposition réactive
Il est possible de concevoir des interfaces à l’aide des dispositions intégrées afin qu’elles passent correctement lorsque l’appareil est pivoté. Lors de la conception d’interfaces qui continueront d’être attrayantes lors de la réponse aux changements d’orientation, tenez compte des règles générales suivantes :
- Attention aux ratios : les changements d’orientation peuvent poser des problèmes lorsque certaines hypothèses sont faites en ce qui concerne les ratios. Par exemple, une vue qui aurait beaucoup d’espace en 1/3 de l’espace vertical d’un écran en portrait peut ne pas tenir dans 1/3 de l’espace vertical dans le paysage.
- Soyez prudent avec les valeurs absolues : les valeurs absolues (pixels) qui sont logiques dans le portrait peuvent ne pas avoir de sens dans le paysage. Lorsque des valeurs absolues sont nécessaires, utilisez des dispositions imbriquées pour isoler leur impact. Par exemple, il serait raisonnable d’utiliser des valeurs absolues dans un
TableView
ItemTemplate
moment où le modèle d’élément a une hauteur uniforme garantie.
Les règles ci-dessus s’appliquent également lors de l’implémentation d’interfaces pour plusieurs tailles d’écran et sont généralement considérées comme recommandées. Le reste de ce guide explique des exemples spécifiques de dispositions réactives à l’aide de chacune des dispositions principales dans Xamarin.Forms.
Remarque
Pour plus de clarté, les sections suivantes montrent comment implémenter des dispositions réactives à l’aide d’un seul type Layout
à la fois. Dans la pratique, il est souvent plus simple de combiner Layout
des s pour obtenir une disposition souhaitée à l’aide de la plus simple ou la plus intuitive Layout
pour chaque composant.
StackLayout
Considérez l’application suivante, affichée dans portrait :
et paysage :
Cela s’effectue avec le code XAML suivant :
<?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>
Certains C# sont utilisés pour modifier l’orientation en fonction de outerStack
l’orientation de l’appareil :
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;
}
}
}
Notez ce qui suit :
outerStack
est ajusté pour présenter l’image et les contrôles sous la forme d’une pile horizontale ou verticale en fonction de l’orientation, afin de tirer le meilleur parti de l’espace disponible.
AbsoluteLayout
Considérez l’application suivante, affichée dans portrait :
et paysage :
Cela s’effectue avec le code XAML suivant :
<?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>
Notez ce qui suit :
- En raison de la façon dont la page a été mise en page, il n’est pas nécessaire que le code procédural introduise la réactivité.
- Il
ScrollView
est utilisé pour permettre à l’étiquette d’être visible même lorsque la hauteur de l’écran est inférieure à la somme des hauteurs fixes des boutons et de l’image.
RelativeLayout
Considérez l’application suivante, affichée dans portrait :
et paysage :
Cela s’effectue avec le code XAML suivant :
<?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>
Notez ce qui suit :
- En raison de la façon dont la page a été mise en page, il n’est pas nécessaire que le code procédural introduise la réactivité.
- Il
ScrollView
est utilisé pour permettre à l’étiquette d’être visible même lorsque la hauteur de l’écran est inférieure à la somme des hauteurs fixes des boutons et de l’image.
Grid
Considérez l’application suivante, affichée dans portrait :
et paysage :
Cela s’effectue avec le code XAML suivant :
<?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>
En plus du code procédural suivant pour gérer les modifications de rotation :
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);
}
}
}
Notez ce qui suit :
- En raison de la façon dont la page a été disposée, il existe une méthode pour modifier l’emplacement de la grille des contrôles.