Compartilhar via


Orientação do Dispositivo

É importante considerar como seu aplicativo será usado e como a orientação horizontal pode ser incorporada para melhorar a experiência do usuário. Layouts individuais podem ser projetados para acomodar várias orientações e melhor usar o espaço disponível. No nível do aplicativo, a rotação pode ser desabilitada ou habilitada.

Orientação de controle

Ao usar Xamarin.Formso , o método suportado para controlar a orientação do dispositivo é usar as configurações para cada projeto individual.

iOS

No iOS, a orientação do dispositivo é configurada para aplicativos que usam o arquivo Info.plist . Use as opções do IDE na parte superior deste documento para selecionar quais instruções você gostaria de ver:

No Visual Studio, abra o projeto iOS e abra Info.plist. O arquivo será aberto em um painel de configuração, começando com a guia Informações de implantação do iPhone:

Informações de implantação do iPhone no Visual Studio

Android

Para controlar a orientação no Android, abra o MainActivity.cs e defina a orientação usando o atributo que decora a 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 suporta várias opções para especificar a orientação:

  • Paisagem – força a orientação do aplicativo a ser paisagem, independentemente dos dados do sensor.
  • Retrato – força a orientação do aplicativo a ser retrato, independentemente dos dados do sensor.
  • Usuário – faz com que o aplicativo seja apresentado usando a orientação preferida do usuário.
  • Atrás – faz com que a orientação do aplicativo seja a mesma da atividade por trás dele.
  • Sensor – faz com que a orientação do aplicativo seja determinada pelo sensor, mesmo que o usuário tenha desativado a rotação automática.
  • SensorLandscape – faz com que o aplicativo use a orientação paisagem ao usar os dados do sensor para alterar a direção que a tela está voltada (para que a tela não seja vista como de cabeça para baixo).
  • SensorPortrait – faz com que o aplicativo use a orientação retrato ao usar os dados do sensor para alterar a direção que a tela está voltada (para que a tela não seja vista como de cabeça para baixo).
  • ReverseLandscape – faz com que o aplicativo use a orientação paisagem, voltada para a direção oposta à usual, de modo a aparecer "de cabeça para baixo".
  • ReversePortrait – faz com que o aplicativo use a orientação retrato, voltada para a direção oposta à usual, de modo a aparecer "de cabeça para baixo".
  • FullSensor – faz com que o aplicativo se baseie nos dados do sensor para selecionar a orientação correta (fora dos 4 possíveis).
  • FullUser – faz com que o aplicativo use as preferências de orientação do usuário. Se a rotação automática estiver ativada, todas as 4 orientações podem ser usadas.
  • UserLandscape[Não suportado] faz com que o aplicativo use a orientação paisagem, a menos que o usuário tenha a rotação automática habilitada, caso em que usará o sensor para determinar a orientação. Esta opção interromperá a compilação.
  • UserPortrait[Não suportado] faz com que o aplicativo use a orientação retrato, a menos que o usuário tenha a rotação automática habilitada, caso em que usará o sensor para determinar a orientação. Esta opção interromperá a compilação.
  • Bloqueado[Não suportado] faz com que o aplicativo use a orientação da tela, seja ela qual for na inicialização, sem responder a alterações na orientação física do dispositivo. Esta opção interromperá a compilação.

Observe que as APIs nativas do Android fornecem muito controle sobre como a orientação é gerenciada, incluindo opções que contradizem explicitamente as preferências expressas do usuário.

Plataforma Universal Windows

Na Plataforma Universal do Windows (UWP), as orientações com suporte são definidas no arquivo Package.appxmanifest . A abertura do manifesto revelará um painel de configuração onde as orientações suportadas podem ser selecionadas.

Reagindo a mudanças de orientação

Xamarin.Forms não oferece nenhum evento nativo para notificar seu aplicativo sobre alterações de orientação no código compartilhado. No entanto,Xamarin.Essentials contém uma classe [DeviceDisplay] que fornece notificações de alterações de orientação.

Para detectar orientações sem Xamarin.Essentials, monitore o SizeChangedPageevento do , que é acionado quando a largura ou a altura das Page alterações são alteradas. Quando a largura do Page é maior que a altura, o dispositivo está no modo paisagem. Para obter mais informações, consulte Exibir uma imagem com base na orientação da tela.

Como alternativa, é possível substituir o OnSizeAllocated método em um Page, inserindo qualquer lógica de alteração de layout lá. O OnSizeAllocated método é chamado sempre que um Page é alocado um novo tamanho, o que acontece sempre que o dispositivo é girado. Observe que a implementação base de executa funções de OnSizeAllocated layout importantes, por isso é importante chamar a implementação base na substituição:

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

Se essa etapa não for executada, a página não funcionará novamente.

Observe que o OnSizeAllocated método pode ser chamado muitas vezes quando um dispositivo é girado. Alterar seu layout a cada vez é desperdício de recursos e pode levar a cintilações. Considere o uso de uma variável de instância em sua página para controlar se a orientação está em paisagem ou retrato e só redesenhe quando houver uma alteração:

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

Depois que uma alteração na orientação do dispositivo for detectada, convém adicionar ou remover exibições adicionais de/para sua interface do usuário para reagir à alteração no espaço disponível. Por exemplo, considere a calculadora integrada em cada plataforma em retrato:

Aplicação de Calculadora em Retrato

e paisagem:

Aplicação de calculadora na paisagem

Observe que os aplicativos aproveitam o espaço disponível adicionando mais funcionalidade na paisagem.

Layout Responsivo

É possível projetar interfaces usando os layouts internos para que eles transitem graciosamente quando o dispositivo é girado. Ao projetar interfaces que continuarão a ser atraentes ao responder a mudanças de orientação, considere as seguintes regras gerais:

  • Preste atenção aos índices – mudanças na orientação podem causar problemas quando certas suposições são feitas em relação aos índices. Por exemplo, um modo de exibição que teria muito espaço em 1/3 do espaço vertical de uma tela em retrato pode não caber em 1/3 do espaço vertical em paisagem.
  • Tenha cuidado com valores absolutos – valores absolutos (pixel) que fazem sentido em retrato podem não fazer sentido em paisagem. Quando valores absolutos forem necessários, use layouts aninhados para isolar seu impacto. Por exemplo, seria razoável usar valores absolutos em um TableViewItemTemplate quando o modelo de item tem uma altura uniforme garantida.

As regras acima também se aplicam ao implementar interfaces para vários tamanhos de tela e são geralmente consideradas práticas recomendadas. O restante deste guia explicará exemplos específicos de layouts responsivos usando cada um dos layouts primários no Xamarin.Forms.

Observação

Para maior clareza, as seções a seguir demonstram como implementar layouts responsivos usando apenas um tipo de Layout de cada vez. Na prática, muitas vezes é mais simples misturar Layouts para alcançar um layout desejado usando o mais simples ou intuitivo Layout para cada componente.

StackLayout

Considere o seguinte aplicativo, exibido em retrato:

A captura de tela mostra o Photo Application StackLayout em retrato.

e paisagem:

A captura de tela mostra Photo Application StackLayout em Landscape.

Isso é feito com o seguinte 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>

Alguns C# são usados para alterar a orientação de outerStack com base na orientação do 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;
        }
    }
}

Observe o seguinte:

  • outerStack é ajustado para apresentar a imagem e os controles como uma pilha horizontal ou vertical, dependendo da orientação, para aproveitar melhor o espaço disponível.

AbsoluteLayout

Considere o seguinte aplicativo, exibido em retrato:

A captura de tela mostra o aplicativo de fotos AbsoluteLayout em retrato.

e paisagem:

A captura de tela mostra o aplicativo de fotos AbsoluteLayout em paisagem.

Isso é feito com o seguinte 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>

Observe o seguinte:

  • Devido à forma como a página foi apresentada, não há necessidade de código processual para introduzir a capacidade de resposta.
  • O ScrollView está sendo usado para permitir que o rótulo seja visível mesmo quando a altura da tela é menor do que a soma das alturas fixas dos botões e da imagem.

RelativeLayout

Considere o seguinte aplicativo, exibido em retrato:

A captura de tela mostra o aplicativo de fotos RelativeLayout em retrato.

e paisagem:

A captura de tela mostra o aplicativo de fotos RelativeLayout em paisagem.

Isso é feito com o seguinte 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>

Observe o seguinte:

  • Devido à forma como a página foi apresentada, não há necessidade de código processual para introduzir a capacidade de resposta.
  • O ScrollView está sendo usado para permitir que o rótulo seja visível mesmo quando a altura da tela é menor do que a soma das alturas fixas dos botões e da imagem.

Grid

Considere o seguinte aplicativo, exibido em retrato:

A captura de tela mostra a grade do aplicativo de fotos em retrato.

e paisagem:

A captura de tela mostra a grade do aplicativo de fotos em paisagem.

Isso é feito com o seguinte 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>

Juntamente com o seguinte código de procedimento para lidar com alterações de rotação:

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

Observe o seguinte:

  • Devido à maneira como a página foi disposta, há um método para alterar o posicionamento da grade dos controles.