Orientacja urządzenia

Download Sample Pobieranie przykładu

Ważne jest, aby wziąć pod uwagę sposób użycia aplikacji oraz sposób włączenia orientacji poziomej w celu ulepszenia środowiska użytkownika. Poszczególne układy można zaprojektować tak, aby pomieścić wiele orientacji i najlepiej wykorzystać dostępne miejsce. Na poziomie aplikacji rotacja może być wyłączona lub włączona.

Kontrolowanie orientacji

W przypadku korzystania z programu Xamarin.Formsobsługiwaną metodą kontrolowania orientacji urządzenia jest użycie ustawień dla każdego projektu.

iOS

W systemie iOS orientacja urządzenia jest skonfigurowana dla aplikacji przy użyciu pliku Info.plist . Użyj opcji środowiska IDE w górnej części tego dokumentu, aby wybrać instrukcje, które chcesz zobaczyć:

W programie Visual Studio otwórz projekt systemu iOS i otwórz plik Info.plist. Plik zostanie otwarty w panelu konfiguracji, począwszy od karty i Telefon Informacje o wdrożeniu:

iPhone Deployment Info in Visual Studio

Android

Aby kontrolować orientację w systemie Android, otwórz MainActivity.cs i ustaw orientację przy użyciu atrybutu MainActivity dekorowania klasy:

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)
...

Platforma Xamarin.Android obsługuje kilka opcji określania orientacji:

  • Poziomo — wymusza orientację aplikacji w poziomie, niezależnie od danych z czujników.
  • Pionowy — wymusza orientację aplikacji na pionowy, niezależnie od danych czujnika.
  • Użytkownik — powoduje, że aplikacja ma być prezentowana przy użyciu preferowanej orientacji użytkownika.
  • Za — powoduje, że orientacja aplikacji jest taka sama jak orientacja działania za nią.
  • Czujnik — powoduje, że orientacja aplikacji jest określana przez czujnik, nawet jeśli użytkownik wyłączył automatyczną rotację.
  • SensorLandscape — powoduje, że aplikacja używa orientacji poziomej podczas używania danych czujnika w celu zmiany kierunku, w którym znajduje się ekran (tak aby ekran nie był postrzegany jako do góry nogami).
  • SensorPortrait — powoduje, że aplikacja używa orientacji pionowej podczas używania danych czujnika w celu zmiany kierunku, w którym jest wyświetlany ekran (tak aby ekran nie był postrzegany jako do góry nogami).
  • ReverseLandscape — powoduje, że aplikacja używa orientacji poziomej, w przeciwnym kierunku niż zwykle, tak aby pojawiała się "do góry nogami".
  • ReversePortrait — powoduje, że aplikacja używa orientacji pionowej, skierowanej do odwrotnego kierunku niż zwykle, tak aby pojawiała się "do góry nogami".
  • FullSensor — powoduje, że aplikacja korzysta z danych czujnika, aby wybrać poprawną orientację (z możliwych 4).
  • FullUser — powoduje, że aplikacja korzysta z preferencji orientacji użytkownika. Jeśli jest włączona automatyczna rotacja, można użyć wszystkich 4 orientacji.
  • UserLandscape[Nieobsługiwane] powoduje, że aplikacja używa orientacji poziomej, chyba że użytkownik ma włączoną automatyczną rotację, w tym przypadku użyje czujnika do określenia orientacji. Ta opcja spowoduje przerwanie kompilacji.
  • UserPortrait[Nieobsługiwane] powoduje, że aplikacja używa orientacji pionowej, chyba że użytkownik ma włączoną automatyczną rotację, w tym przypadku użyje czujnika do określenia orientacji. Ta opcja spowoduje przerwanie kompilacji.
  • Zablokowane — [Nieobsługiwane] powoduje, że aplikacja korzysta z orientacji ekranu, bez odpowiadania na zmiany orientacji fizycznej urządzenia. Ta opcja spowoduje przerwanie kompilacji.

Należy pamiętać, że natywne interfejsy API systemu Android zapewniają dużą kontrolę nad sposobem zarządzania orientacją, w tym opcjami jawnie sprzecznymi z wyrażonymi preferencjami użytkownika.

Platforma uniwersalna systemu Windows

W platforma uniwersalna systemu Windows (UWP) obsługiwane orientacje są ustawiane w pliku Package.appxmanifest. Otwarcie manifestu spowoduje wyświetlenie panelu konfiguracji, w którym można wybrać obsługiwane orientacje.

Reagowanie na zmiany w orientacji

Xamarin.Forms nie oferuje żadnych zdarzeń natywnych do powiadamiania aplikacji o zmianach orientacji w kodzie udostępnionym. Xamarin.Essentials Zawiera jednak klasę [DeviceDisplay], która dostarcza powiadomienia o zmianach orientacji.

Aby wykryć orientacje bez Xamarin.Essentials, monitoruj SizeChanged zdarzenie Page, które jest uruchamiane, gdy szerokość lub wysokość Page zmian. Gdy szerokość obiektu Page jest większa niż wysokość, urządzenie jest w trybie poziomym. Aby uzyskać więcej informacji, zobacz Wyświetlanie obrazu na podstawie orientacji ekranu.

Alternatywnie można zastąpić metodę OnSizeAllocated w Pageobiekcie , wstawiając tam dowolną logikę zmiany układu. Metoda OnSizeAllocated jest wywoływana za każdym razem, gdy zostanie przydzielony nowy rozmiar, co ma miejsce za każdym Page razem, gdy urządzenie jest obracane. Należy pamiętać, że podstawowa implementacja OnSizeAllocated funkcji układu wykonuje ważne funkcje układu, dlatego ważne jest, aby wywołać implementację podstawową w zastąpieniu:

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

Nie można wykonać tego kroku, co spowoduje, że strona nie działa.

Należy pamiętać, że OnSizeAllocated metoda może być wywoływana wiele razy, gdy urządzenie jest obracane. Zmiana układu za każdym razem jest marudna zasobów i może prowadzić do migotania. Rozważ użycie zmiennej wystąpienia na stronie, aby śledzić, czy orientacja znajduje się w orientacji poziomej, czy pionowej, i rysuj ponownie tylko w przypadku zmiany:

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

Po wykryciu zmiany orientacji urządzenia możesz dodać lub usunąć dodatkowe widoki do/z interfejsu użytkownika, aby zareagować na zmianę dostępnego miejsca. Rozważmy na przykład wbudowany kalkulator na każdej platformie w orientacji pionowej:

Calculator Application in Portrait

i poziome:

Calculator Application in Landscape

Zwróć uwagę, że aplikacje korzystają z dostępnego miejsca, dodając więcej funkcji w środowisku poziomym.

Układ dynamiczny

Interfejsy można projektować przy użyciu wbudowanych układów, aby bezpiecznie przechodziły po obróceniu urządzenia. Podczas projektowania interfejsów, które będą nadal atrakcyjne podczas reagowania na zmiany orientacji, należy wziąć pod uwagę następujące ogólne reguły:

  • Zwróć uwagę na współczynniki — zmiany orientacji mogą powodować problemy, gdy niektóre założenia są podejmowane w odniesieniu do współczynników. Na przykład widok, który miałby dużo miejsca w 1/3 pionowej przestrzeni ekranu w orientacji pionowej, może nie mieścić się w 1/3 pionowej przestrzeni poziomej.
  • Zachowaj ostrożność przy użyciu wartości bezwzględnych — wartości bezwzględne (pikseli), które mają sens w orientacji pionowej, mogą nie mieć sensu w krajobrazie. Jeśli wartości bezwzględne są niezbędne, użyj układów zagnieżdżonych, aby odizolować ich wpływ. Na przykład uzasadnione byłoby użycie wartości bezwzględnych w TableViewItemTemplate szablonie elementu z gwarantowaną jednolitą wysokością.

Powyższe reguły mają zastosowanie również podczas implementowania interfejsów dla wielu rozmiarów ekranu i są ogólnie uważane za najlepsze rozwiązanie. W pozostałej części tego przewodnika wyjaśniono konkretne przykłady dynamicznych układów przy użyciu każdego z układów podstawowych w programie Xamarin.Forms.

Uwaga

W celu zapewnienia przejrzystości w poniższych sekcjach pokazano, jak zaimplementować dynamiczne układy przy użyciu tylko jednego typu Layout w danym momencie. W praktyce często łatwiej jest mieszać Layouts w celu osiągnięcia żądanego układu przy użyciu prostszego lub najbardziej intuicyjnego Layout dla każdego składnika.

StackLayout

Rozważmy następującą aplikację wyświetlaną w orientacji pionowej:

Screenshot shows Photo Application StackLayout in Portrait.

i poziome:

Screenshot shows Photo Application StackLayout in Landscape.

Jest to realizowane przy użyciu następującego kodu 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>

Niektóre język C# służy do zmieniania orientacji outerStack na podstawie orientacji urządzenia:

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

Należy zwrócić uwagę na następujące kwestie:

  • outerStack jest dostosowywany do prezentowania obrazu i kontrolek jako stosu poziomego lub pionowego w zależności od orientacji, aby najlepiej wykorzystać dostępne miejsce.

AbsoluteLayout

Rozważmy następującą aplikację wyświetlaną w orientacji pionowej:

Screenshot shows Photo Application AbsoluteLayout in Portrait.

i poziome:

Screenshot shows Photo Application AbsoluteLayout in Landscape.

Jest to realizowane przy użyciu następującego kodu 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>

Należy zwrócić uwagę na następujące kwestie:

  • Ze względu na sposób, w jaki strona została określona, nie ma potrzeby, aby kod proceduralny wprowadzał czas odpowiedzi.
  • Element ScrollView jest używany do zezwalania na widoczność etykiety nawet wtedy, gdy wysokość ekranu jest mniejsza niż suma stałych wysokości przycisków i obrazu.

RelativeLayout

Rozważmy następującą aplikację wyświetlaną w orientacji pionowej:

Screenshot shows Photo Application RelativeLayout in Portrait.

i poziome:

Screenshot shows Photo Application RelativeLayout in Landscape.

Jest to realizowane przy użyciu następującego kodu 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>

Należy zwrócić uwagę na następujące kwestie:

  • Ze względu na sposób, w jaki strona została określona, nie ma potrzeby, aby kod proceduralny wprowadzał czas odpowiedzi.
  • Element ScrollView jest używany do zezwalania na widoczność etykiety nawet wtedy, gdy wysokość ekranu jest mniejsza niż suma stałych wysokości przycisków i obrazu.

Siatka

Rozważmy następującą aplikację wyświetlaną w orientacji pionowej:

Screenshot shows Photo Application Grid in Portrait.

i poziome:

Screenshot shows Photo Application Grid in Landscape.

Jest to realizowane przy użyciu następującego kodu 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>

Wraz z następującym kodem proceduralnym do obsługi zmian rotacji:

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

Należy zwrócić uwagę na następujące kwestie:

  • Ze względu na sposób, w jaki strona została ułożona, istnieje metoda zmiany położenia siatki kontrolek.