Ориентация устройства

Download Sample Скачайте пример

Важно учитывать, как будет использоваться приложение и как можно включить альбомную ориентацию для улучшения взаимодействия с пользователем. Отдельные макеты можно разрабатывать для размещения нескольких ориентаций и лучше всего использовать доступное пространство. На уровне приложения поворот можно отключить или включить.

Управление ориентацией

При использовании Xamarin.Formsподдерживаемый метод управления ориентацией устройства — использовать параметры для каждого отдельного проекта.

iOS

В iOS ориентация устройства настраивается для приложений с помощью файла Info.plist . Используйте параметры интегрированной среды разработки в верхней части этого документа, чтобы выбрать нужные инструкции:

В Visual Studio откройте проект iOS и откройте info.plist. Файл откроется на панели конфигурации, начиная с вкладки сведений о развертывании i Телефон:

iPhone Deployment Info in Visual Studio

Android

Чтобы управлять ориентацией в Android, откройте MainActivity.cs и задайте ориентацию с помощью атрибута, украшающего 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 поддерживает несколько вариантов указания ориентации:

  • Ландшафт — заставляет ориентацию приложения быть альбомной, независимо от данных датчика.
  • Книжная — заставляет ориентацию приложения быть книжной, независимо от данных датчика.
  • Пользователь — приводит к тому, что приложение будет представлено с помощью предпочтительной ориентации пользователя.
  • Позади — приводит к тому, что ориентация приложения совпадает с ориентацией действия за ним.
  • Датчик — приводит к тому, что ориентация приложения определяется датчиком, даже если пользователь отключил автоматическую смену.
  • SensorLandscape — позволяет приложению использовать альбомную ориентацию при использовании данных датчика для изменения направления экрана (так что экран не отображается как перевернутый).
  • SensorPortrait — позволяет приложению использовать книжную ориентацию при использовании данных датчика для изменения направления экрана (так что экран не отображается как перевернутый).
  • ReverseLandscape — заставляет приложение использовать альбомную ориентацию, сталкиваясь с противоположной стороной от обычной, чтобы показаться "вверх с ног".
  • ReversePortrait — приводит к тому, что приложение использует книжную ориентацию, сталкиваясь с противоположной стороной от обычного, чтобы появиться "вверх с ног".
  • FullSensor — позволяет приложению полагаться на данные датчика, чтобы выбрать правильную ориентацию (из возможного 4).
  • FullUser — приводит к тому, что приложение будет использовать настройки ориентации пользователя. Если включена автоматическая смена, можно использовать все 4 ориентации.
  • UserLandscape[Не поддерживается] заставляет приложение использовать альбомную ориентацию, если пользователь не включил автоматическую смену, в этом случае датчик будет использоваться для определения ориентации. Этот параметр приведет к прерыванию компиляции.
  • UserPortrait[Не поддерживается] приводит к тому, что приложение будет использовать книжную ориентацию, если пользователь не включил автоматическую смену, в этом случае датчик будет использовать датчик для определения ориентации. Этот параметр приведет к прерыванию компиляции.
  • Заблокировано . [Не поддерживается] приложение будет использовать ориентацию экрана, независимо от того, что она запущена, без реагирования на изменения физической ориентации устройства. Этот параметр приведет к прерыванию компиляции.

Обратите внимание, что собственные API Android обеспечивают много контроля над управлением ориентацией, включая параметры, которые явно противоречат выраженным предпочтениям пользователя.

Универсальная платформа Windows

В универсальная платформа Windows (UWP) поддерживаемые ориентации задаются в файле Package.appxmanifest. Открытие манифеста покажет панель конфигурации, в которой можно выбрать поддерживаемые ориентации.

Реагирование на изменения в ориентации

Xamarin.Forms не предлагает собственные события для уведомления приложения об изменениях ориентации в общем коде. Однако содержит класс [DeviceDisplay],Xamarin.Essentials предоставляющий уведомления об изменениях ориентации.

Чтобы обнаружить ориентацию без Xamarin.Essentials, отслеживайте SizeChanged событие Page, которое срабатывает при изменении ширины или высоты Page . Если ширина превышает Page высоту, устройство находится в альбомном режиме. Дополнительные сведения см. в разделе "Отображение изображения на основе ориентации экрана".

Кроме того, можно переопределить OnSizeAllocated метод на объекте, вставляя в нее Pageлогику изменения макета. Метод OnSizeAllocated вызывается всякий раз Page , когда выделяется новый размер, который происходит при смене устройства. Обратите внимание, что базовая реализация выполняет важные функции макета OnSizeAllocated , поэтому важно вызвать базовую реализацию в переопределении:

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

Сбой этого шага приведет к неработоспособности страницы.

Обратите внимание, что OnSizeAllocated метод может вызываться много раз при повороте устройства. Изменение макета каждый раз является расточительным из-за ресурсов и может привести к мерцаниям. Попробуйте использовать переменную экземпляра на странице для отслеживания того, находится ли ориентация в альбомной или книжной, и перерисовка только при изменении:

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

После обнаружения изменения ориентации устройства может потребоваться добавить или удалить дополнительные представления в пользовательский интерфейс для реагирования на изменение доступного пространства. Например, рассмотрим встроенный калькулятор на каждой платформе в книжном режиме:

Calculator Application in Portrait

и ландшафт:

Calculator Application in Landscape

Обратите внимание, что приложения используют доступное пространство, добавив дополнительные функциональные возможности в альбомной среде.

Адаптивный макет

Интерфейсы можно создавать с помощью встроенных макетов, чтобы они плавно переходили при повороте устройства. При проектировании интерфейсов, которые будут оставаться привлекательными при реагировании на изменения в ориентации, учитывайте следующие общие правила:

  • Обратите внимание на коэффициенты — изменения в ориентации могут вызвать проблемы, когда некоторые предположения делаются в отношении соотношений. Например, представление, которое будет иметь много места в 1/3 вертикального пространства экрана в портрете может не помещаться в 1/3 вертикального пространства в ландшафте.
  • Будьте осторожны с абсолютными значениями — абсолютными (пиксель) значениями , которые имеет смысл в портрете, могут не иметь смысла в ландшафте. Если необходимы абсолютные значения, используйте вложенные макеты для изоляции их влияния. Например, было бы разумно использовать абсолютные значения в TableViewItemTemplate шаблоне элемента с гарантированной равномерной высотой.

Приведенные выше правила также применяются при реализации интерфейсов для нескольких размеров экрана и обычно считаются рекомендациями. В остальной части этого руководства приводятся конкретные примеры адаптивных макетов с помощью каждого из основных макетов.Xamarin.Forms

Примечание.

Для ясности в следующих разделах показано, как реализовать адаптивные макеты с помощью одного типа Layout одновременно. На практике часто проще смешивать Layouts для достижения требуемого макета с помощью более простого или наиболее интуитивно понятного Layout для каждого компонента.

StackLayout

Рассмотрим следующее приложение, отображаемое в книжном режиме:

Screenshot shows Photo Application StackLayout in Portrait.

и ландшафт:

Screenshot shows Photo Application StackLayout in Landscape.

Это достигается с помощью следующего 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>

Некоторые C# используются для изменения ориентации outerStack на основе ориентации устройства:

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

Обратите внимание на следующее:

  • outerStack настраивается для представления изображения и элементов управления в виде горизонтального или вертикального стека в зависимости от ориентации, чтобы лучше всего воспользоваться доступным пространством.

AbsoluteLayout

Рассмотрим следующее приложение, отображаемое в книжном режиме:

Screenshot shows Photo Application AbsoluteLayout in Portrait.

и ландшафт:

Screenshot shows Photo Application AbsoluteLayout in Landscape.

Это достигается с помощью следующего 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>

Обратите внимание на следующее:

  • Из-за того, как страница была размещена, нет необходимости в процедурных кодах для внедрения скорости реагирования.
  • Используется ScrollView для отображения метки даже в том случае, если высота экрана меньше суммы фиксированных высот кнопок и изображения.

RelativeLayout

Рассмотрим следующее приложение, отображаемое в книжном режиме:

Screenshot shows Photo Application RelativeLayout in Portrait.

и ландшафт:

Screenshot shows Photo Application RelativeLayout in Landscape.

Это достигается с помощью следующего 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>

Обратите внимание на следующее:

  • Из-за того, как страница была размещена, нет необходимости в процедурных кодах для внедрения скорости реагирования.
  • Используется ScrollView для отображения метки даже в том случае, если высота экрана меньше суммы фиксированных высот кнопок и изображения.

Сетка

Рассмотрим следующее приложение, отображаемое в книжном режиме:

Screenshot shows Photo Application Grid in Portrait.

и ландшафт:

Screenshot shows Photo Application Grid in Landscape.

Это достигается с помощью следующего 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>

Наряду со следующим процедурным кодом для обработки изменений поворота:

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

Обратите внимание на следующее:

  • Из-за способа размещения страницы существует метод изменения размещения сетки элементов управления.