Orientation de l’appareil

Télécharger l’exemple Télécharger l’exemple

Il est important de considérer comment 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 au 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.Formsde , la méthode prise en charge pour contrôler l’orientation des appareils consiste à utiliser les paramètres de chaque projet individuel.

iOS

Sur iOS, l’orientation des appareils 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 info.plist. Le fichier s’ouvre dans un panneau de configuration, en commençant par l’onglet Informations sur le déploiement de l’iPhone :

Informations sur le déploiement d’iPhone dans Visual Studio

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 : fait en sorte que l’orientation de l’application soit la même que l’orientation de l’activité qui la sous-tend.
  • Capteur : permet de déterminer l’orientation de l’application par le capteur, même si l’utilisateur a désactivé la rotation automatique.
  • SensorLandscape : amène l’application à utiliser l’orientation paysage 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 : amène l’application à utiliser l’orientation portrait lors de l’utilisation des données du capteur pour modifier la direction de l’écran (de sorte que l’écran ne soit pas considéré comme à l’envers).
  • ReverseLandscape : amène l’application à utiliser l’orientation paysage, dans la direction opposée à celle habituelle, afin d’apparaître « à l’envers ».
  • ReversePortrait : amène l’application à utiliser l’orientation portrait, face à la direction opposée à la normale, afin d’apparaître « à l’envers ».
  • FullSensor : oblige l’application à s’appuyer sur les données du capteur pour sélectionner l’orientation correcte (sur les 4 possibles).
  • FullUser : amène l’application à utiliser les préférences d’orientation de l’utilisateur. Si la rotation automatique est activée, les 4 orientations peuvent être utilisées.
  • UserLandscape: [Non pris en charge] oblige l’application à utiliser l’orientation paysage, sauf si la rotation automatique est activée, auquel cas l’utilisateur utilise le capteur pour déterminer l’orientation. Cette option interrompt la compilation.
  • UserPortrait: [Non pris en charge] oblige l’application à utiliser l’orientation portrait, sauf si la rotation automatique est activée, auquel cas l’utilisateur utilise le capteur pour déterminer l’orientation. Cette option interrompt la compilation.
  • Verrouillé: [Non pris en charge] oblige l’application à utiliser l’orientation de l’écran, quelle qu’elle 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 un contrôle important sur la façon dont l’orientation est gérée, y compris des 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 changements d’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 de Page, qui se déclenche lorsque la largeur ou la Page hauteur des modifications sont effectuées. Lorsque la largeur de est Page supérieure à la hauteur, l’appareil est en mode paysage. Pour plus d’informations, consultez Afficher une image en fonction de l’orientation de l’écran.

Vous pouvez également remplacer la OnSizeAllocated méthode sur un Page, en y insérant une logique de modification de disposition. La OnSizeAllocated méthode est appelée chaque fois qu’une nouvelle taille est allouée à un Page , ce qui se produit chaque fois que l’appareil est pivoté. Notez que l’implémentation de base de exécute 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
}

Si vous n’effectuez pas cette étape, une page ne fonctionne pas.

Notez que la OnSizeAllocated méthode peut être appelée plusieurs fois lorsqu’un appareil est pivoté. La modification de votre disposition à chaque fois est une perte de ressources et peut entraîner un scintillement. Envisagez d’utiliser une variable instance dans votre page pour déterminer si l’orientation est en paysage ou en portrait, et redessinez 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 vers/depuis votre interface utilisateur pour réagir à la modification de l’espace disponible. Par exemple, considérez la calculatrice intégrée sur chaque plateforme dans le portrait :

Application calculatrice dans portrait

paysage et :

Application calculatrice dans le 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 effectuent une transition normale lors de la rotation de l’appareil. 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 :

  • Faites attention aux ratios : les changements d’orientation peuvent entraîner des problèmes lorsque certaines hypothèses sont faites en ce qui concerne les ratios. Par exemple, une vue qui aurait beaucoup d’espace dans 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 ont un sens 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 TableViewItemTemplate lorsque 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 une bonne pratique. Le reste de ce guide explique des exemples spécifiques de dispositions réactives utilisant chacune des dispositions principales dans Xamarin.Forms.

Notes

Pour plus de clarté, les sections suivantes montrent comment implémenter des dispositions réactives à l’aide d’un seul type de à la Layout fois. Dans la pratique, il est souvent plus simple de combiner Layoutdes pour obtenir une disposition souhaitée en utilisant la plus simple ou la plus intuitive Layout pour chaque composant.

StackLayout

Considérez l’application suivante, affichée dans portrait :

Capture d’écran montrant Photo Application StackLayout dans Portrait.

paysage et :

Capture d’écran montrant Photo Application StackLayout dans 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>

Un certain C# est utilisé pour modifier l’orientation de 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 les points suivants :

  • 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 :

Capture d’écran montrant Photo Application AbsoluteLayout dans Portrait.

paysage et :

Capture d’écran montrant Photo Application AbsoluteLayout dans 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 les points suivants :

  • En raison de la façon dont la page a été mise en page, il n’est pas nécessaire d’utiliser un code procédural pour introduire une réactivité.
  • le 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 :

Capture d’écran montrant Photo Application RelativeLayout dans Portrait.

et paysage :

Capture d’écran montrant l’application photo RelativeLayout dans paysage.

Cela est effectué 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 les points suivants :

  • En raison de la façon dont la page a été disposée, il n’est pas nécessaire d’utiliser un code procédural pour introduire la réactivité.
  • 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.

Grille

Considérez l’application suivante, affichée dans portrait :

Capture d’écran montrant Photo Application Grid dans Portrait.

et paysage :

Capture d’écran montrant Photo Application Grid dans Paysage.

Cela est effectué 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>

Avec le 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 les points suivants :

  • En raison de la façon dont la page a été disposée, il existe une méthode permettant de modifier l’emplacement de la grille des contrôles.