Obsługa różnych rozmiarów wyświetlaczy umożliwia dostęp do aplikacji na wielu różnych urządzeniach i większej liczbie użytkowników.
Aby obsługiwać jak najwięcej rozmiarów wyświetlaczy – zarówno różnych ekranów urządzeń , jak i różnych okien aplikacji w trybie wielu okien – zaprojektuj układy aplikacji tak, aby były elastyczne i adaptacyjne. Układy elastyczne i adaptacyjne zapewniają optymalne wrażenia użytkownika niezależnie od rozmiaru wyświetlacza. Dzięki temu aplikacja może działać na telefonach, tabletach, urządzeniach składanych, urządzeniach z ChromeOS, w orientacji pionowej i poziomej oraz w konfiguracjach z możliwością zmiany rozmiaru wyświetlacza, takich jak tryb podzielonego ekranu i tryb okien na pulpicie.
Układy elastyczne i adaptacyjne zmieniają się w zależności od dostępnego miejsca na wyświetlaczu. Zmiany obejmują drobne korekty układu, które wypełniają miejsce (projektowanie elastyczne), oraz całkowite zastąpienie jednego układu innym, aby aplikacja mogła jak najlepiej dostosować się do różnych rozmiarów wyświetlaczy (projektowanie adaptacyjne).
Jetpack Compose jako deklaratywny zestaw narzędzi do tworzenia interfejsu użytkownika idealnie nadaje się do projektowania i implementowania układów, które dynamicznie się zmieniają, aby inaczej renderować treści na wyświetlaczach o różnych rozmiarach.
Wyraźne wprowadzanie dużych zmian w układzie w przypadku komponentów kompozycyjnych na poziomie treści
Komponenty kompozycyjne na poziomie aplikacji i treści zajmują całą przestrzeń wyświetlacza dostępną dla aplikacji. W przypadku tych typów komponentów kompozycyjnych warto zmienić ogólny układ aplikacji na dużych wyświetlaczach.
Unikaj używania wartości sprzętu fizycznego do podejmowania decyzji dotyczących układu. Może się wydawać, że warto podejmować decyzje na podstawie stałej wartości materialnej (czy urządzenie jest tabletem? Czy fizyczny ekran ma określony współczynnik proporcji?), ale odpowiedzi na te pytania mogą nie być przydatne do określenia miejsca dostępnego dla interfejsu użytkownika.
Na tabletach aplikacja może działać w trybie wielu okien, co oznacza, że może dzielić ekran z inną aplikacją. W trybie okien na pulpicie lub w ChromeOS aplikacja może znajdować się w oknie o zmiennym rozmiarze. Może też być więcej niż 1 ekran fizyczny, np. w przypadku urządzenia składanego. We wszystkich tych przypadkach fizyczny rozmiar ekranu nie ma znaczenia przy podejmowaniu decyzji o sposobie wyświetlania treści.
Zamiast tego podejmuj decyzje na podstawie rzeczywistej części ekranu przydzielonej do Twojej aplikacji, która jest opisana przez bieżące dane okna dostarczane przez bibliotekę Jetpack WindowManager. Przykład użycia WindowManager w aplikacji Compose znajdziesz w przykładzie JetNews.
Dostosowanie układów do dostępnego miejsca na wyświetlaczu zmniejsza też ilość specjalnej obsługi wymaganej do obsługi platform takich jak ChromeOS oraz formatów takich jak tablety i urządzenia składane.
Gdy określisz dane miejsca dostępnego dla aplikacji, przekonwertuj surowy rozmiar na klasę rozmiaru okna zgodnie z opisem w sekcji Używanie klas rozmiaru okna. Klasy rozmiaru okna to punkty przerwania zaprojektowane tak, aby zachować równowagę między prostotą logiki aplikacji a elastycznością optymalizacji aplikacji pod kątem większości rozmiarów wyświetlaczy.
Klasy rozmiaru okna odnoszą się do całego okna aplikacji, dlatego używaj ich do podejmowania decyzji dotyczących układu, które wpływają na ogólny układ aplikacji. Możesz przekazywać klasy rozmiaru okna jako stan lub wykonywać dodatkową logikę, aby utworzyć stan pochodny, który będzie przekazywany do zagnieżdżonych komponentów kompozycyjnych.
@Composable fun MyApp( windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true).windowSizeClass ) { // Decide whether to show the top app bar based on window size class. val showTopAppBar = windowSizeClass.isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND) // MyScreen logic is based on the showTopAppBar boolean flag. MyScreen( showTopAppBar = showTopAppBar, /* ... */ ) }
Podejście warstwowe ogranicza logikę rozmiaru wyświetlacza do jednej lokalizacji, zamiast rozpraszać ją w wielu miejscach aplikacji, które muszą być zsynchronizowane. Jedna lokalizacja tworzy stan, który można jawnie przekazywać do innych komponentów kompozycyjnych tak jak każdy inny stan aplikacji. Jawne przekazywanie stanu upraszcza poszczególne komponenty kompozycyjne, ponieważ pobierają one klasę rozmiaru okna lub określoną konfigurację wraz z innymi danymi.
Elastyczne zagnieżdżone komponenty kompozycyjne można używać wielokrotnie
Komponenty kompozycyjne są bardziej uniwersalne, gdy można je umieszczać w wielu różnych miejscach. Jeśli komponent kompozycyjny musi być umieszczony w określonym miejscu o określonym rozmiarze, prawdopodobnie nie będzie można go użyć w innych kontekstach. Oznacza to też, że poszczególne komponenty kompozycyjne do ponownego wykorzystania nie powinny w sposób dorozumiany zależeć od globalnych informacji o rozmiarze wyświetlacza.
Wyobraź sobie zagnieżdżony komponent kompozycyjny, który implementuje układ szczegółowa lista. Może on wyświetlać jeden panel lub 2 panele obok siebie:
Decyzja dotycząca układu lista-szczegóły powinna być częścią ogólnego układu aplikacji, dlatego jest przekazywana z komponentu kompozycyjnego na poziomie treści:
@Composable fun AdaptivePane( showOnePane: Boolean, /* ... */ ) { if (showOnePane) { OnePane(/* ... */) } else { TwoPane(/* ... */) } }
Co zrobić, jeśli chcesz, aby komponent kompozycyjny niezależnie zmieniał swój układ w zależności od dostępnego miejsca na wyświetlaczu, np. karta, która wyświetla dodatkowe szczegóły, jeśli jest na to miejsce? Chcesz wykonać jakąś logikę na podstawie dostępnego rozmiaru wyświetlacza, ale jakiego konkretnie?
Unikaj używania rozmiaru rzeczywistego ekranu urządzenia. Nie będzie on dokładny w przypadku różnych typów ekranów, a także wtedy, gdy aplikacja nie jest wyświetlana na pełnym ekranie.
Ponieważ komponent kompozycyjny nie jest komponentem kompozycyjnym na poziomie treści, nie używaj bezpośrednio bieżących danych okna.
Jeśli komponent jest umieszczony z dopełnieniem (np. z wcięciami) lub jeśli aplikacja zawiera komponenty takie jak pasek nawigacyjny czy pasek aplikacji, ilość miejsca na wyświetlaczu dostępnego dla komponentu kompozycyjnego może się znacznie różnić od ogólnej ilości miejsca dostępnego dla aplikacji.
Użyj szerokości, którą komponent kompozycyjny faktycznie ma do renderowania. Masz 2 opcje uzyskania tej szerokości:
Jeśli chcesz zmienić miejsce lub sposób wyświetlania treści, użyj kolekcji modyfikatorów lub niestandardowego układu, aby układ był elastyczny. Może to być tak proste, jak wypełnienie przez element podrzędny całego dostępnego miejsca lub ułożenie elementów podrzędnych w kilku kolumnach, jeśli jest wystarczająco dużo miejsca.
Jeśli chcesz zmienić to, co wyświetlasz, użyj
BoxWithConstraintsjako bardziej zaawansowanej alternatywy.BoxWithConstraintsudostępnia ograniczenia pomiaru, których możesz użyć do wywoływania różnych komponentów kompozycyjnych na podstawie dostępnego miejsca na wyświetlaczu. Ma to jednak pewne wady, ponieważBoxWithConstraintsodracza kompozycję do fazy układu, gdy te ograniczenia są znane, co powoduje, że podczas układu wykonywana jest większa ilość pracy.
@Composable fun Card(/* ... */) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(/* ... */) Title(/* ... */) } } else { Row { Column { Title(/* ... */) Description(/* ... */) } Image(/* ... */) } } } }
Udostępnianie wszystkich danych w przypadku różnych rozmiarów wyświetlaczy
Podczas implementowania komponentu kompozycyjnego, który wykorzystuje dodatkowe miejsce na wyświetlaczu, możesz chcieć zaoszczędzić czas i wczytywać dane jako efekt uboczny bieżącego rozmiaru wyświetlacza.
Takie działanie jest jednak sprzeczne z zasadą jednokierunkowego przepływu danych, zgodnie z którą dane można przenosić i udostępniać komponentom kompozycyjnym, aby je odpowiednio renderować. Komponent kompozycyjny powinien otrzymywać wystarczającą ilość danych, aby zawsze miał wystarczającą ilość treści na każdy rozmiar wyświetlacza, nawet jeśli część treści nie zawsze będzie używana.
@Composable fun Card( imageUrl: String, title: String, description: String ) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(imageUrl) Title(title) } } else { Row { Column { Title(title) Description(description) } Image(imageUrl) } } } }
W przykładzie Card zwróć uwagę, że description jest zawsze przekazywany do Card. Mimo że description jest używany tylko wtedy, gdy szerokość pozwala na jego wyświetlenie, Card zawsze wymaga description, niezależnie od dostępnej szerokości.
Stałe przekazywanie wystarczającej ilości treści upraszcza układy adaptacyjne, ponieważ są one mniej zależne od stanu, i zapobiega wywoływaniu efektów ubocznych podczas przełączania się między rozmiarami wyświetlaczy (co może się zdarzyć w wyniku zmiany rozmiaru okna, zmiany orientacji lub składania i rozkładania urządzenia).
Ta zasada umożliwia też zachowanie stanu podczas zmian układu. Przenosząc informacje, które mogą nie być używane w przypadku wszystkich rozmiarów wyświetlaczy, możesz zachować stan aplikacji podczas zmiany rozmiaru układu.
Możesz na przykład przenieść flagę logiczną showMore, aby stan aplikacji był zachowywany, gdy zmiana rozmiaru wyświetlacza powoduje przełączanie się układu między ukrywaniem a wyświetlaniem treści:
@Composable fun Card( imageUrl: String, title: String, description: String ) { var showMore by remember { mutableStateOf(false) } BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(imageUrl) Title(title) } } else { Row { Column { Title(title) Description( description = description, showMore = showMore, onShowMoreToggled = { newValue -> showMore = newValue } ) } Image(imageUrl) } } } }
Więcej informacji
Więcej informacji o układach adaptacyjnych w Compose znajdziesz w tych materiałach:
Przykładowe aplikacje
- CanonicalLayouts to repozytorium sprawdzonych wzorców projektowych które zapewniają optymalne wrażenia użytkownika na dużych wyświetlaczach.
- JetNews pokazuje, jak zaprojektować aplikację, która dostosowuje swój interfejs użytkownika do dostępnego miejsca na wyświetlaczu.
- Reply to adaptacyjny przykład obsługi urządzeń mobilnych, tabletów i urządzeń składanych.
- Now in Android to aplikacja, która używa układów adaptacyjnych do obsługi różnych rozmiarów wyświetlaczy.
Filmy