Aplikacje mobilne · Responsywność

Dostosowanie do platformy i rozmiaru ekranu

Ten sam kod MAUI działa na telefonie, tablecie, Androidzie, iOS i Windows, ale „działa” nie znaczy „wygląda dobrze”. Poznasz trzy poziomy dostosowania: OnPlatform, DeviceInfo i układ zależny od szerokości okna.

  • około 60 minut
  • .NET MAUI · OnPlatform · DeviceInfo · VisualStateManager
  • INF.04.6.2

Po tej lekcji: ustawić różne wartości dla różnych platform, odczytać informacje o urządzeniu i zmienić układ strony za pomocą AdaptiveTrigger i zdarzenia SizeChanged.

Jeden ekran działający w pionie i na szeroko

Przeciągnij krawędź okna: jeśli tekst znika, sama zmiana rozmiaru czcionki nie wystarczy. Ułożymy te same panele pod sobą albo obok siebie.

Najpierw uruchomisz mały ekran. Potem połączysz go z kodem i sprawdzisz konkretne wyniki. Dodatki wykonuj dopiero, gdy wersja podstawowa działa. Quiz i oceniane zadania pozostają w biurku po zalogowaniu.

Laboratorium · ćwiczenie w przeglądarce, do którego odsyłają rozgrzewki na końcu lekcji

1 · Sprawdź w działaniu

Zmieniaj urządzenie i szerokość okna

Przesuwaj suwak szerokości. Około 700 px układ przełącza się z jednej kolumny na dwie. Wybierz „Tablet z Androidem”: system to nadal Android, ale układ jest szeroki. Włącz też trzeci stan i rozciągnij okno powyżej 1000 px.

Najpierw przewidź, potem sprawdź: Przesuń szerokość w modelu przez próg700. Wskaż osobno zmianę kolumn Grid i zmianę miejsca panelu. To są dwa uzupełniające działania.

Ta interakcja wymaga włączonego JavaScriptu.

O układzie decyduje rzeczywista szerokość, a nie nazwa systemu. OnPlatform służy do drobnych różnic wartości (np. margines pod notchem iPhone’a). Model pomaga zobaczyć mechanizm; właściwą aplikację uruchomisz z plików poniżej.

1. Przygotuj miejsce pracy

Co dodajesz, a co podmieniasz?

Stronę dodaj przez Projekt → Dodaj → Nowy element → .NET MAUI ContentPage (XAML). Powstaje para .xaml i .xaml.cs. Dla ContentView wybierz szablon ContentView; dla modeli i serwisów zwykłą klasę. Jeśli plik już istnieje, podmień wskazaną zawartość — nie twórz drugiej klasy o tej samej nazwie.

PlikRola w ćwiczeniu
MainPage.xamlWygląd strony lub kontrolki
MainPage.xaml.csLogika i obsługa danych

Zanim uruchomisz: trzy połączenia

x:Class="ResponsywnaMauiApp.NazwaStrony" w XAML musi zgadzać się z namespace ResponsywnaMauiApp i nazwą klasy C#. InitializeComponent() tworzy kontrolki opisane w XAML.

x:Name="PoleImie" daje kodowi C# dostęp przez PoleImie.Text. Z kolei Clicked="Zapisz_Clicked" wskazuje metodę o tej nazwie w pliku .xaml.cs. Sam napis przycisku w Text niczego nie podpina.

Gdy dodajesz XAML z obsługą zdarzeń, od razu uzupełnij też odpowiadający C#. Uruchamiaj dopiero po skończeniu oznaczonej pary plików; brakujący handler nie jest błędem środowiska.

2. Uruchom sam wygląd — jeszcze bez logiki

W świeżym projekcie zastąp MainPage.xaml poniższym ekranem. W MainPage.xaml.cs zachowaj konstruktor z InitializeComponent(); usuń szablonowy licznik i OnCounterClicked, bo nie ma ich już w XAML. Na tym etapie App i AppShell pozostają z szablonu. To pełny, tymczasowy XAML — później świadomie go podmienisz, nie dokleisz do drugiego ContentPage.

MainPage.xaml — etap tymczasowyxaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ResponsywnaMauiApp.MainPage">
    <VerticalStackLayout Padding="24" Spacing="14">
        <Label Text="Jeden ekran, różne rozmiary" FontSize="24" />
        <Border Padding="20" Stroke="#4EE0B5">
        <Label Text="Panel informacji" />
        </Border>
        <Border Padding="20" Stroke="#8F7BFF">
        <Label Text="Panel treści" />
        </Border>
    </VerticalStackLayout>
</ContentPage>
  1. Pierwsza próba Uruchom. Zobaczysz kontrolki, ale przyciski bez Clicked jeszcze nic nie robią. Sprawdź rozmiar i kolejność elementów. Zatrzymaj aplikację przed następnym etapem.

Poczta na telefonie i na tablecie

Jedna lista czy lista i szczegóły obok siebie?

Aplikacja pocztowa na telefonie pokazuje listę wiadomości na całym ekranie, a szczegóły na osobnym. Na tablecie jest dość miejsca, by pokazać oba panele jednocześnie. Ta sama aplikacja zmienia więc układ siatki między „jedna kolumna” a „dwie kolumny”, i to dokładnie robi przykład z lekcji.

Kluczowa zasada: nie zakładaj, że „Android” to telefon. Są tablety z Androidem, iPady z iOS, a okno aplikacji na Windows może mieć dowolną szerokość.

2 · Od prostego do elastycznego

Co kiedy stosować

PoziomNarzędzieDo czego
1OnPlatform w XAMLRóżne wartości tej samej właściwości dla platform (marginesy, rozmiary).
2DeviceInfo.CurrentInformacje o urządzeniu w kodzie: Platform, Idiom, Model, VersionString, DeviceType.
3aVisualStateManager + AdaptiveTriggerDeklaratywna zmiana układu po przekroczeniu progu szerokości (MinWindowWidth).
3bZdarzenie SizeChangedUkład zmieniany z kodu, gdy logika jest zbyt złożona dla triggerów.
Proste różnice wartości zapisuj w `OnPlatform`, a nie w warunkach `if (DeviceInfo.Platform == ...)`.

3. Wskaż stronę startową ćwiczenia

Po dodaniu docelowych stron podmień App.xaml.cs na poniższy. App.xaml zostaje z szablonu. Od tej chwili startuje MainPage, a nie wcześniejszy tymczasowy MainPage z AppShell. Uzupełnij jej pliki w kolejnych etapach, zanim ponownie uruchomisz projekt.

App.xaml.cscsharp
namespace ResponsywnaMauiApp;

public partial class App : Application
{
    public App() => InitializeComponent();
    protected override Window CreateWindow(IActivationState? activationState)
        => new Window(new NavigationPage(new MainPage()));
}
  1. CreateWindow Tworzy okno aplikacji. NavigationPage jest kontenerem zwykłych stron i daje przycisk powrotu; nie łączymy go z Shell.
  2. new MainPage() Tworzy instancję docelowej strony. Aby testować drugi niezależny przykład, zatrzymaj program, zmień tylko ten typ i uruchom ponownie.

4. Połącz wygląd z działaniem

Teraz zbuduj docelowy przykład. Pełne pliki poniżej zastępują swoje wcześniejsze wersje. Przeczytaj opis nad kodem i objaśnienia pod nim; potem sprawdź przepływ danych, nie tylko brak czerwonych podkreśleń.

Gdy przykład ma kilka plików zależnych od siebie, najpierw dodaj wszystkie wskazane typy, a potem uruchom całość. Ustaw breakpoint w obsłudze głównego przycisku i prześledź jedną próbę.

Budowa krok po kroku

MainPage.xaml

Najpierw sprawdź strukturę Grid, potem VisualStateGroup. Stan Telefon ma jedną kolumnę, Tablet dwie. OnPlatform wybiera odstęp według systemu, nie szerokości.

Margines zależny od platformy i siatka zmieniająca definicje kolumn oraz wierszy po przekroczeniu 700 px.

MainPage.xamlxaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ResponsywnaMauiApp.MainPage"
             Title="Responsywny ekran">

    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness">
            <On Platform="Android" Value="18" />
            <On Platform="iOS" Value="18,50,18,18" />
            <On Platform="WinUI" Value="30" />
        </OnPlatform>
    </ContentPage.Padding>

    <Grid x:Name="GlownyGrid" RowDefinitions="Auto,*" ColumnDefinitions="*"
          RowSpacing="16" ColumnSpacing="16">

        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="SzerokoscEkranu">
                <VisualState Name="Telefon">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="ColumnDefinitions" Value="*" />
                        <Setter Property="RowDefinitions" Value="Auto,*" />
                    </VisualState.Setters>
                </VisualState>

                <VisualState Name="Tablet">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="700" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="ColumnDefinitions" Value="280,*" />
                        <Setter Property="RowDefinitions" Value="*" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <Border Padding="18" Stroke="#808080">
            <VerticalStackLayout>
                <Label Text="Urządzenie" FontAttributes="Bold" />
                <Label x:Name="UrzadzenieLabel" />
            </VerticalStackLayout>
        </Border>

        <Border x:Name="TrescPanel" Grid.Row="1" Padding="18" Stroke="#808080">
            <VerticalStackLayout>
                <Label Text="Treść aplikacji" FontSize="24" FontAttributes="Bold" />
                <Label Text="Na telefonie panel jest pod informacjami. Na szerokim ekranie znajduje się obok." />
            </VerticalStackLayout>
        </Border>
    </Grid>
</ContentPage>
  1. <OnPlatform x:TypeArguments="Thickness"> Wartość zależna od systemu. iOS dostaje większy margines górny (50) z powodu notcha i paska statusu.
  2. VisualStateGroup i VisualState Stan wizualny to nazwany zestaw ustawień. W grupie aktywny jest jeden naraz.
  3. AdaptiveTrigger MinWindowWidth="700" Stan „Tablet” włącza się, gdy okno ma co najmniej 700 jednostek szerokości.
  4. Setter Property="ColumnDefinitions" Telefon: jedna kolumna i dwa wiersze. Tablet: kolumny 280,* i jeden wiersz.

Budowa krok po kroku

MainPage.xaml.cs

SizeChanged przestawia panel do właściwego wiersza i kolumny. Nie zastępuje definicji siatki z XAML. Oba progi700 muszą być takie same.

Odczyt danych o urządzeniu oraz przestawienie panelu treści po zmianie rozmiaru.

MainPage.xaml.cscsharp
using Microsoft.Maui.Devices;

namespace ResponsywnaMauiApp;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        SizeChanged += OnSizeChanged;
        PokazInformacje();
    }

    private void PokazInformacje()
    {
        UrzadzenieLabel.Text =
            $"Platforma: {DeviceInfo.Current.Platform}\n" +
            $"Typ: {DeviceInfo.Current.Idiom}\n" +
            $"Model: {DeviceInfo.Current.Model}\n" +
            $"System: {DeviceInfo.Current.VersionString}\n" +
            $"Emulator: {DeviceInfo.Current.DeviceType == DeviceType.Virtual}";
    }

    private void OnSizeChanged(object? sender, EventArgs e)
    {
        bool szerokiEkran = Width >= 700;

        Grid.SetRow(TrescPanel, szerokiEkran ? 0 : 1);
        Grid.SetColumn(TrescPanel, szerokiEkran ? 1 : 0);
    }
}
  1. DeviceInfo.Current.Idiom Typ urządzenia: Phone, Tablet lub Desktop.
  2. DeviceType == DeviceType.Virtual Odróżnia emulator od prawdziwego urządzenia. Przydatne, bo GPS i aparat działają tam inaczej.
  3. \n w interpolacji Znak nowej linii. Bez ukośnika zobaczysz literę „n” sklejoną z tekstem.
  4. SizeChanged += OnSizeChanged Zdarzenie strony zgłaszane przy każdej zmianie rozmiaru.
  5. Grid.SetRow / Grid.SetColumn Ustawiają pozycję elementu w siatce z kodu.
  6. Dwa mechanizmy naraz W tym przykładzie działają niezależnie. W realnym projekcie wybierz jeden na dany fragment, aby nie ustawiały tej samej właściwości sprzecznie.

5. Odbiór: czy naprawdę działa?

Wykonaj próby w swojej aplikacji, nie tylko w modelu na stronie. Zaznaczaj dopiero po sprawdzeniu wyniku. Funkcje urządzenia wymagają testu na właściwej platformie.

5 · Czego unikać

Trzy błędy z responsywnością

Platforma = typ urządzenia
Tak nie
if (DeviceInfo.Platform == DevicePlatform.Android) UkladTelefonu();

Tablet z Androidem dostaje układ telefonu.

Tak
bool szerokiEkran = Width >= 700;

Decyduje rzeczywista szerokość okna.

Test na jednym rozmiarze
Tak nie
// sprawdzone tylko na jednym emulatorze telefonu

Na szerszym ekranie układ się „łamie” i nikt tego nie zauważa.

Tak
// test: wąski telefon, tablet i okno Windows o zmiennej szerokości

Sprawdzaj oba końce spektrum.

if zamiast OnPlatform
Tak nie
if (DeviceInfo.Platform == DevicePlatform.iOS) Padding = new Thickness(18, 50, 18, 18);

Działa, ale to zbędna komplikacja dla prostej różnicy wartości.

Tak
<On Platform="iOS" Value="18,50,18,18" />

Deklaratywny OnPlatform jest czytelniejszy.

Ćwiczenie z pomocą · samodzielna praca

6. Mała zmiana, którą zrobisz samodzielnie

Zmień próg układu na800 i sprawdź obie strony granicy.

Oczekiwany rezultat
Zmień próg układu na800 i sprawdź obie strony granicy.
Dotychczasowe działanie ma pozostać poprawne.
Potrzebujesz pomocy? Rozwiązuj krok po kroku.
Krok 1 — pokaż podpowiedź

Zmień MinWindowWidth stanu Tablet.

Krok 2 — pokaż podpowiedź

Zmień również warunek Width >= 700 w C#, bo przestawia on panel.

Krok 3 — pokaż podpowiedź

Porównaj799 i800; pozostawienie dwóch różnych progów rozjedzie układ.

Otwórz tylko tyle podpowiedzi, ile potrzebujesz. Po każdym kroku wróć do zadania i sprawdź wynik.

6 · Sprawdź się

Które narzędzie?

Cztery sytuacje.

Ta interakcja wymaga włączonego JavaScriptu.

Podsumowanie

Co warto zapamiętać

  1. OnPlatform zmienia wartości zależnie od systemu, dobrze sprawdza się przy marginesach.
  2. DeviceInfo.Current zwraca platformę, typ urządzenia (Idiom), model i informację o emulatorze.
  3. O układzie decyduje szerokość okna: VisualStateManager + AdaptiveTrigger albo SizeChanged.
  4. Nie zakładaj, że Android to telefon. Testuj wąski i szeroki ekran.

Dalej na swoim koncie

Rozgrzewki, quiz i zadania są w biurku ucznia.

Teoria jest otwarta dla wszystkich. Żeby rozwiązywać zadania, zapisywać szkice i widzieć swój postęp, zaloguj się.

Zaloguj się →
Źródła i dalsza nauka