Orientación del dispositivo

Download SampleDescargar el ejemplo

Es importante tener en cuenta cómo se usará la aplicación y cómo se puede incorporar la orientación horizontal para mejorar la experiencia del usuario. Se pueden crear diseños individuales para adaptarse a varias orientaciones y usar mejor el espacio disponible. A nivel de aplicación, la rotación se puede deshabilitar o habilitar.

Control de la orientación

Cuando se usa Xamarin.Forms, el método admitido para controlar la orientación del dispositivo es usar la configuración de cada proyecto individual.

iOS

En iOS, la orientación del dispositivo se configurada para las aplicaciones mediante el archivo Info.plist. Use las opciones del IDE de la parte superior de este documento para seleccionar las instrucciones que desea ver:

En Visual Studio, abra el proyecto de iOS y el archivo Info.plist. El archivo se abrirá en un panel de configuración, empezando por la pestaña de información de implementación del iPhone:

iPhone Deployment Info in Visual Studio

Android

Para controlar la orientación en Android, abra MainActivity.cs y establezca la orientación mediante el atributo que decora la clase 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 admite varias opciones para especificar la orientación:

  • Horizontal: fuerza la orientación de la aplicación a horizontal, independientemente de los datos del sensor.
  • Vertical: fuerza la orientación de la aplicación a vertical, independientemente de los datos del sensor.
  • Usuario: hace que la aplicación se presente mediante la orientación preferida del usuario.
  • Detrás: hace que la orientación de la aplicación sea la misma que la orientación de la actividad detrás de ella.
  • Sensor: hace que el sensor determine la orientación de la aplicación, incluso si el usuario ha deshabilitado la rotación automática.
  • SensorLandscape: hace que la aplicación use la orientación horizontal mientras se usan los datos del sensor para cambiar la dirección hacia la que está orientada la pantalla (para evitar que la pantalla se vea bocabajo).
  • SensorPortrait: hace que la aplicación use la orientación vertical mientras se usan los datos del sensor para cambiar la dirección hacia la que está orientada la pantalla (para evitar que la pantalla se vea bocarriba).
  • ReverseLandscape: hace que la aplicación use la orientación horizontal, en dirección opuesta a la habitual, para que aparezca "bocabajo".
  • ReversePortrait: hace que la aplicación use la orientación vertical, en dirección opuesta a la habitual, para que aparezca "bocabajo".
  • FullSensor: hace que la aplicación se base en los datos del sensor para seleccionar la orientación correcta (de las 4 posibles).
  • FullUser: hace que la aplicación use las preferencias de orientación del usuario. Si la rotación automática está habilitada, se pueden usar las 4 orientaciones.
  • UserLandscape: [no compatible] hace que la aplicación use la orientación horizontal, a menos que el usuario tenga habilitada la rotación automática, en cuyo caso usará el sensor para determinar la orientación. Esta opción interrumpirá la compilación.
  • UserPortrait: [no compatible] hace que la aplicación use la orientación vertical, a menos que el usuario tenga habilitada la rotación automática, en cuyo caso usará el sensor para determinar la orientación. Esta opción interrumpirá la compilación.
  • Bloqueado : [no compatible] hace que la aplicación use la orientación de la pantalla, sea cual sea al inicio, sin responder a los cambios en la orientación física del dispositivo. Esta opción interrumpirá la compilación.

Tenga en cuenta que las API nativas de Android proporcionan un gran control sobre cómo se administra la orientación, incluidas las opciones que contradicen explícitamente las preferencias expresadas por el usuario.

Plataforma universal de Windows

En la Plataforma universal de Windows (UWP), las orientaciones admitidas se establecen en el archivo Package.appxmanifest. Al abrir el manifiesto se mostrará un panel de configuración en el que se pueden seleccionar las orientaciones admitidas.

Reacción a los cambios en la orientación

Xamarin.Forms no ofrece ningún evento nativo para notificar a la aplicación los cambios de orientación en el código compartido. Sin embargo,Xamarin.Essentials contiene una clase [DeviceDisplay] que proporciona notificaciones de los cambios de orientación.

Para detectar orientaciones sin Xamarin.Essentials, supervise el evento SizeChanged de Page, que se desencadena cuando cambia el ancho o el alto de Page. Cuando el ancho de Page es mayor que el alto, el dispositivo está en modo horizontal. Para más información, consulte Visualización de una imagen según la orientación de la pantalla.

Como alternativa, es posible invalidar el método OnSizeAllocated en un elemento Pageinsertando allí cualquier lógica de cambio de diseño. Se llama al método OnSizeAllocated cada vez que se asigna a un elemento Pageun nuevo tamaño, lo que sucede cada vez que se gira el dispositivo. Tenga en cuenta que la implementación base de OnSizeAllocated realiza funciones de diseño importantes, por lo que es fundamental llamar a la implementación base en la invalidación:

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

Si no se lleva a cabo ese paso, se generará una página que no funciona.

Tenga en cuenta que se puede llamar al método OnSizeAllocated muchas veces cuando se gira un dispositivo. Cambiar el diseño cada vez es un desperdicio de recursos y puede provocar parpadeo. Considere la posibilidad de usar una variable de instancia dentro de la página para rastrear si la orientación está en horizontal o vertical y solo actualizarla cuando haya un cambio:

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

Una vez detectado un cambio en la orientación del dispositivo, es posible que quiera agregar o quitar vistas adicionales en la interfaz de usuario para reaccionar al cambio en el espacio disponible. Por ejemplo, considere la calculadora integrada en cada plataforma en vertical:

Calculator Application in Portrait

y en horizontal:

Calculator Application in Landscape

Tenga en cuenta que las aplicaciones aprovechan el espacio disponible agregando más funcionalidad en modo horizontal.

Diseño dinámico

Es posible diseñar interfaces usando los diseños integrados para que realicen la transición correctamente cuando se gira el dispositivo. Al diseñar interfaces que seguirán siendo atractivas al responder a los cambios en la orientación, tenga en cuenta las siguientes reglas generales:

  • Preste atención a las relaciones de aspecto: los cambios en la orientación pueden causar problemas cuando se realizan ciertas suposiciones con respecto a las relaciones de aspecto. Por ejemplo, una vista que tendría mucho espacio en 1/3 del espacio vertical de una pantalla en vertical podría no caber en 1/3 del espacio vertical en horizontal.
  • Tenga cuidado con los valores absolutos: los valores absolutos (píxeles) que tienen sentido en vertical pueden no tener sentido en horizontal. Cuando se necesiten valores absolutos, use diseños anidados para aislar su efecto. Por ejemplo, sería razonable usar valores absolutos en un elemento TableViewItemTemplate cuando la plantilla de elemento tenga un alto uniforme garantizado.

Las reglas anteriores también se aplican al implementar interfaces para varios tamaños de pantalla y generalmente se consideran procedimientos recomendados. En el resto de esta guía se explicarán ejemplos específicos de diseños dinámicos usando cada uno de los diseños principales de Xamarin.Forms.

Nota:

Para mayor claridad, en las secciones siguientes se muestra cómo implementar diseños dinámicos con solo un tipo de Layout cada vez. En la práctica, a menudo es más sencillo mezclar elementos Layout para lograr el diseño deseado usando el valor de Layout más simple e intuitivo para cada componente.

StackLayout

Considere la siguiente aplicación, que se muestra en vertical:

Screenshot shows Photo Application StackLayout in Portrait.

y en horizontal:

Screenshot shows Photo Application StackLayout in Landscape.

Esto se logra con el código XAML siguiente:

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

Se usa algo de código de C# para cambiar la orientación de outerStack en función de la orientación 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;
        }
    }
}

Tenga en cuenta lo siguiente:

  • outerStack se ajusta para presentar la imagen y los controles como una pila horizontal o vertical en función de la orientación, para aprovechar mejor el espacio disponible.

AbsoluteLayout

Considere la siguiente aplicación, que se muestra en vertical:

Screenshot shows Photo Application AbsoluteLayout in Portrait.

y en horizontal:

Screenshot shows Photo Application AbsoluteLayout in Landscape.

Esto se logra con el código XAML siguiente:

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

Tenga en cuenta lo siguiente:

  • Debido a la forma en que se ha diseñado la página, no es necesario que el código de procedimiento introduzca la capacidad de respuesta.
  • ScrollView se usa para permitir que la etiqueta sea visible incluso cuando el alto de la pantalla es menor que la suma de las alturas fijas de los botones y la imagen.

RelativeLayout

Considere la siguiente aplicación, que se muestra en vertical:

Screenshot shows Photo Application RelativeLayout in Portrait.

y en horizontal:

Screenshot shows Photo Application RelativeLayout in Landscape.

Esto se logra con el código XAML siguiente:

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

Tenga en cuenta lo siguiente:

  • Debido a la forma en que se ha diseñado la página, no es necesario que el código de procedimiento introduzca la capacidad de respuesta.
  • ScrollView se usa para permitir que la etiqueta sea visible incluso cuando el alto de la pantalla es menor que la suma de las alturas fijas de los botones y la imagen.

Grid

Considere la siguiente aplicación, que se muestra en vertical:

Screenshot shows Photo Application Grid in Portrait.

y en horizontal:

Screenshot shows Photo Application Grid in Landscape.

Esto se logra con el código XAML siguiente:

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

Junto con el código de procedimientos siguiente para controlar los cambios de rotación:

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

Tenga en cuenta lo siguiente:

  • Debido a la forma en que se ha diseñado la página, hay un método para cambiar la ubicación de la cuadrícula de los controles.