Orientacja urządzenia
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 Informacje o wdrożeniu telefonu iPhone:
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 Page
obiekcie , 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:
i poziome:
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
TableView
ItemTemplate
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ć Layout
s 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:
i poziome:
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:
i poziome:
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:
i poziome:
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:
i poziome:
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.