Orientamento del dispositivo

È importante considerare come verrà usata l'applicazione e come integrare l'orientamento orizzontale per migliorare l'esperienza utente. I singoli layout possono essere progettati per supportare più orientamenti e usare al meglio lo spazio disponibile. A livello di applicazione, la rotazione può essere disabilitata o abilitata.

Controllo dell'orientamento

Quando si usa Xamarin.Forms, il metodo supportato per controllare l'orientamento del dispositivo consiste nell'usare le impostazioni per ogni singolo progetto.

iOS

In iOS l'orientamento del dispositivo è configurato per le applicazioni usando il file Info.plist . Usare le opzioni dell'IDE nella parte superiore di questo documento per selezionare le istruzioni da visualizzare:

In Visual Studio aprire il progetto iOS e aprire Info.plist. Il file verrà aperto in un pannello di configurazione, a partire dalla scheda i Telefono Informazioni sulla distribuzione:

i Telefono Informazioni sulla distribuzione in Visual Studio

Android

Per controllare l'orientamento in Android, aprire MainActivity.cs e impostare l'orientamento usando l'attributo che decora 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 supporta diverse opzioni per specificare l'orientamento:

  • Orizzontale : forza l'orientamento dell'applicazione in orizzontale, indipendentemente dai dati del sensore.
  • Verticale : forza l'orientamento dell'applicazione in verticale, indipendentemente dai dati del sensore.
  • Utente : fa in modo che l'applicazione venga presentata usando l'orientamento preferito dell'utente.
  • Dietro: fa sì che l'orientamento dell'applicazione corrisponda all'orientamento dell'attività sottostante.
  • Sensore : determina l'orientamento dell'applicazione dal sensore, anche se l'utente ha disabilitato la rotazione automatica.
  • SensorLandscape : fa sì che l'applicazione usi l'orientamento orizzontale durante l'uso dei dati del sensore per modificare la direzione in cui si trova lo schermo (in modo che lo schermo non venga considerato capovolto).
  • SensorPortrait : fa sì che l'applicazione usi l'orientamento verticale durante l'uso dei dati del sensore per modificare la direzione in cui si trova lo schermo (in modo che lo schermo non venga considerato capovolto).
  • ReverseLandscape : fa sì che l'applicazione usi l'orientamento orizzontale, rivolta verso la direzione opposta dal solito, in modo da apparire "capovolto".
  • ReversePortrait : fa sì che l'applicazione usi l'orientamento verticale, rivolta verso la direzione opposta dal solito, in modo da apparire "capovolto".
  • FullSensor : fa sì che l'applicazione si basi sui dati del sensore per selezionare l'orientamento corretto (fuori dal possibile 4).
  • FullUser : fa sì che l'applicazione usi le preferenze di orientamento dell'utente. Se la rotazione automatica è abilitata, è possibile usare tutti e 4 gli orientamenti.
  • UserLandscape : [Non supportato] fa sì che l'applicazione usi l'orientamento orizzontale, a meno che l'utente non abbia abilitato la rotazione automatica, nel qual caso userà il sensore per determinare l'orientamento. Questa opzione interromperà la compilazione.
  • UserPortrait : [Non supportato] fa sì che l'applicazione usi l'orientamento verticale, a meno che l'utente non abbia abilitato la rotazione automatica, nel qual caso userà il sensore per determinare l'orientamento. Questa opzione interromperà la compilazione.
  • Bloccato : [Non supportato] fa sì che l'applicazione usi l'orientamento dello schermo, indipendentemente dal momento dell'avvio, senza rispondere alle modifiche apportate all'orientamento fisico del dispositivo. Questa opzione interromperà la compilazione.

Si noti che le API Android native forniscono un sacco di controllo sul modo in cui viene gestito l'orientamento, incluse le opzioni che contraddicono in modo esplicito le preferenze espresse dall'utente.

Piattaforma UWP (Universal Windows Platform)

Nel piattaforma UWP (Universal Windows Platform) (UWP), gli orientamenti supportati vengono impostati nel file Package.appxmanifest. Aprendo il manifesto verrà visualizzato un pannello di configurazione in cui è possibile selezionare gli orientamenti supportati.

Reazione ai cambiamenti nell'orientamento

Xamarin.Forms non offre eventi nativi per notificare all'app modifiche di orientamento nel codice condiviso. Contiene tuttaviaXamarin.Essentials una classe [DeviceDisplay] che fornisce notifiche di modifiche all'orientamento.

Per rilevare gli orientamenti senza Xamarin.Essentials, monitorare l'evento SizeChanged di Page, che viene generato quando la larghezza o l'altezza delle Page modifiche. Quando la larghezza di è maggiore dell'altezza Page , il dispositivo è in modalità orizzontale. Per altre informazioni, vedere Visualizzare un'immagine basata sull'orientamento dello schermo.

In alternativa, è possibile eseguire l'override del OnSizeAllocated metodo in un Pageoggetto , inserendo qualsiasi logica di modifica del layout. Il OnSizeAllocated metodo viene chiamato ogni volta che viene Page allocata una nuova dimensione, che si verifica ogni volta che il dispositivo viene ruotato. Si noti che l'implementazione di base di esegue importanti funzioni di OnSizeAllocated layout, quindi è importante chiamare l'implementazione di base nell'override:

protected override void OnSizeAllocated(double width, double height)
{
    base.OnSizeAllocated(width, height); //must be called
}

Se non si esegue questo passaggio, verrà visualizzata una pagina non funzionante.

Si noti che il OnSizeAllocated metodo può essere chiamato molte volte quando un dispositivo viene ruotato. La modifica del layout ogni volta che è sprecato di risorse e può causare sfarfallio. È consigliabile usare una variabile di istanza all'interno della pagina per tenere traccia dell'orientamento orizzontale o verticale e ridisegnare solo quando si verifica una modifica:

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

Dopo aver rilevato una modifica dell'orientamento del dispositivo, è possibile aggiungere o rimuovere visualizzazioni aggiuntive da/verso l'interfaccia utente per reagire alla modifica dello spazio disponibile. Si consideri, ad esempio, il calcolatore predefinito in ogni piattaforma in verticale:

Applicazione calcolatrice in verticale

e orizzontale:

Applicazione calcolatrice in orizzontale

Si noti che le app sfruttano lo spazio disponibile aggiungendo altre funzionalità in orizzontale.

Layout reattivo

È possibile progettare interfacce usando i layout predefiniti in modo che vengano trasferiti normalmente quando il dispositivo viene ruotato. Quando si progettano interfacce che continueranno ad essere accattivanti quando rispondono alle modifiche nell'orientamento, considerare le regole generali seguenti:

  • Prestare attenzione ai rapporti : i cambiamenti nell'orientamento possono causare problemi quando vengono fatti determinati presupposti per quanto riguarda i rapporti. Ad esempio, una visualizzazione che avrebbe un sacco di spazio in 1/3 dello spazio verticale di uno schermo in verticale potrebbe non rientrare in 1/3 dello spazio verticale in orizzontale.
  • Prestare attenzione ai valori assoluti : valori assoluti (pixel) che hanno senso nel verticale potrebbero non avere senso nel panorama. Quando sono necessari valori assoluti, usare layout annidati per isolarne l'impatto. Ad esempio, sarebbe ragionevole usare valori assoluti in un TableViewItemTemplate quando il modello di elemento ha un'altezza uniforme garantita.

Le regole precedenti si applicano anche quando si implementano interfacce per più dimensioni dello schermo e sono generalmente considerate procedure consigliate. Nella parte restante di questa guida verranno illustrati esempi specifici di layout reattivi usando ognuno dei layout principali in Xamarin.Forms.

Nota

Per maggiore chiarezza, le sezioni seguenti illustrano come implementare layout reattivi usando un solo tipo di Layout alla volta. In pratica, è spesso più semplice combinare Layouts per ottenere un layout desiderato usando il più semplice o più intuitivo Layout per ogni componente.

StackLayout

Si consideri l'applicazione seguente, visualizzata in verticale:

Screenshot che mostra Photo Application StackLayout in Verticale.

e orizzontale:

Screenshot shows Photo Application StackLayout in Landscape.

Questa operazione viene eseguita con il codice XAML seguente:

<?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>

Alcuni C# vengono usati per modificare l'orientamento di outerStack in base all'orientamento del dispositivo:

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

Nota quanto segue:

  • outerStack viene regolato in modo da presentare l'immagine e i controlli come uno stack orizzontale o verticale a seconda dell'orientamento, per sfruttare al meglio lo spazio disponibile.

AbsoluteLayout

Si consideri l'applicazione seguente, visualizzata in verticale:

Screenshot che mostra Photo Application AbsoluteLayout in Verticale.

e orizzontale:

Screenshot che mostra Photo Application AbsoluteLayout in Landscape.

Questa operazione viene eseguita con il codice XAML seguente:

<?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>

Nota quanto segue:

  • A causa del modo in cui è stata disposta la pagina, non è necessario che il codice procedurale introduca velocità di risposta.
  • Viene ScrollView usato per consentire la visibilità dell'etichetta anche quando l'altezza dello schermo è inferiore alla somma delle altezze fisse dei pulsanti e dell'immagine.

RelativeLayout

Si consideri l'applicazione seguente, visualizzata in verticale:

Screenshot che mostra Photo Application RelativeLayout in Verticale.

e orizzontale:

Screenshot shows Photo Application RelativeLayout in Landscape.

Questa operazione viene eseguita con il codice XAML seguente:

<?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>

Nota quanto segue:

  • A causa del modo in cui è stata disposta la pagina, non è necessario che il codice procedurale introduca velocità di risposta.
  • Viene ScrollView usato per consentire la visibilità dell'etichetta anche quando l'altezza dello schermo è inferiore alla somma delle altezze fisse dei pulsanti e dell'immagine.

Griglia

Si consideri l'applicazione seguente, visualizzata in verticale:

Screenshot che mostra la griglia dell'applicazione foto in verticale.

e orizzontale:

Screenshot che mostra La griglia dell'applicazione foto in orizzontale.

Questa operazione viene eseguita con il codice XAML seguente:

<?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>

Insieme al codice procedurale seguente per gestire le modifiche di rotazione:

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

Nota quanto segue:

  • A causa della disposizione della pagina, esiste un metodo per modificare il posizionamento della griglia dei controlli.