Obsługa różnych rozmiarów ekranów umożliwia dostęp do aplikacji na największą liczbę urządzeń i dla największej liczby użytkowników.
Aby obsługiwać jak najwięcej rozmiarów ekranu – czy to ekranów różnych urządzeń, czy różnych okien aplikacji w trybie wielookienkowym – zaprojektuj układy aplikacji tak, aby były elastyczne i dopasowywały się do różnych rozmiarów. Elastyczne układy zapewniają optymalne wrażenia użytkownika niezależnie od rozmiaru ekranu, dzięki czemu aplikacja może działać na telefonach, tabletach, składanych urządzeniach, urządzeniach z ChromeOS, w orientacji pionowej i poziomej oraz w konfiguracjach z możliwością zmiany rozmiaru ekranu, takich jak tryb podzielonego ekranu i okna na pulpicie.
Elastyczne/adaptacyjne układy zmieniają się w zależności od dostępnej przestrzeni wyświetlania. Zmiany te obejmują od drobnych korekt układu, które wypełniają wolną przestrzeń (projekt elastyczny), po całkowite zastąpienie jednego układu innym, aby aplikacja mogła lepiej dostosować się do różnych rozmiarów ekranów (projekt adaptacyjny).
Jako deklaratywny zestaw narzędzi do tworzenia interfejsu Jetpack Compose doskonale nadaje się do projektowania i wdrażania układów, które dynamicznie zmieniają się, aby renderować zawartość w różny sposób na ekranach o różnych rozmiarach.
Dodawanie wyraźnych zmian układu w przypadku komponentów na poziomie treści
Komponenty na poziomie aplikacji i komponenty na poziomie treści zajmują całą dostępną przestrzeń wyświetlania aplikacji. W przypadku tego typu komponentów warto zmienić ogólny układ aplikacji na dużych ekranach.
Unikaj używania wartości sprzętowych do podejmowania decyzji dotyczących układu. Możesz być skłonny podejmować decyzje na podstawie stałej wartości (czy urządzenie to tablet? Czy fizyczny ekran ma określony format?), ale odpowiedzi na te pytania mogą nie być przydatne do określenia dostępnej przestrzeni dla interfejsu.

Na tabletach aplikacja może działać w trybie wielookiennym, co oznacza, że może dzielić ekran z inną aplikacją. W trybie okna na komputerze lub w ChromeOS aplikacja może być w oknie, którego rozmiar można zmieniać. Może być nawet 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 należy podejmować decyzje na podstawie rzeczywistej części ekranu przydzielonej aplikacji, opisanej przez bieżące dane o oknie udostępniane przez bibliotekę Jetpacka WindowManager. Przykład użycia WindowManager w aplikacji Compose znajdziesz w aplikacji JetNews.
Utworzenie układów dostosowanych do dostępnej przestrzeni wyświetlania zmniejsza też ilość specjalnych operacji potrzebnych do obsługi platform takich jak ChromeOS oraz formatów takich jak tablety i urządzenia składane.
Po określeniu danych dotyczących przestrzeni dostępnej dla aplikacji przekonwertuj rozmiar surowy na klasę rozmiaru okna zgodnie z opisem w artykule Używanie klas rozmiaru okna. Klasy rozmiarów okna to punkty graniczne, które umożliwiają zachowanie równowagi między prostotą logiki aplikacji a elastycznością optymalizacji aplikacji pod kątem większości rozmiarów wyświetlacza. Klasy rozmiaru okna odnoszą się do całego okna aplikacji, więc używaj ich do podejmowania decyzji dotyczących układu, które mają wpływ na ogólny układ aplikacji. Klasy windowsize możesz przekazywać jako stan lub wykonać dodatkową logikę, aby utworzyć stan pochodzenia, który będzie przekazywany do zagnieżdżonych komponentów.
@Composable fun MyApp( windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().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, /* ... */ ) }
Dzięki podejściu warstwowemu logika rozmiaru wyświetlania jest ograniczona do jednego miejsca, zamiast rozpraszania jej w wielu miejscach w aplikacji, które trzeba utrzymywać w zsynchronizowanym stanie. Pojedyncza lokalizacja generuje stan, który można jawnie przekazywać innym komponentom, tak jak każdy inny stan aplikacji. Przekazywanie stanu w wyraźny sposób upraszcza działanie poszczególnych komponentów, ponieważ wraz z innymi danymi przyjmują one klasę rozmiaru okna lub określoną konfigurację.
Elastyczne składniki zagłębione można ponownie wykorzystać
Elementy składane można wielokrotnie wykorzystywać, ponieważ można je umieszczać w różnych miejscach. Jeśli kompozyt musi być umieszczony w określonej lokalizacji o określonym rozmiarze, prawdopodobnie nie będzie można go używać w innych kontekstach. Oznacza to również, że pojedyncze komponenty wielokrotnego użytku nie powinny pośrednio zależeć od globalnych informacji o rozmiarze wyświetlacza.
Wyobraź sobie zagnieżdżony komponent, który implementuje układ listy i szczegółów. Może on wyświetlać jedną lub 2 półki obok siebie:

Decyzja dotycząca listy i szczegółów powinna być częścią ogólnego układu aplikacji, dlatego jest przekazywana z komponowalnych komponentów na poziomie treści:
@Composable fun AdaptivePane( showOnePane: Boolean, /* ... */ ) { if (showOnePane) { OnePane(/* ... */) } else { TwoPane(/* ... */) } }
Co zrobić, jeśli chcesz, aby komponent samodzielnie zmieniał układ w zależności od dostępnej przestrzeni wyświetlania, na przykład aby karta wyświetlała dodatkowe szczegóły, jeśli pozwala na to miejsce? Chcesz wykonać pewną logikę na podstawie dostępnego rozmiaru wyświetlania, ale jakiego dokładnie?

Nie używaj rzeczywistego rozmiaru ekranu urządzenia. Nie będzie to dokładne w przypadku różnych typów ekranów ani wtedy, gdy aplikacja nie jest wyświetlana na pełnym ekranie.
Ponieważ komponent nie jest komponentem na poziomie treści, nie używaj bezpośrednio bieżących danych o oknie. Jeśli komponent jest umieszczony z odstępem (np. w przypadku wstawek) lub jeśli aplikacja zawiera komponenty takie jak paski nawigacyjne czy paski aplikacji, ilość miejsca na wyświetlaczu dostępna dla kompozytowego komponentu może się znacznie różnić od ogólnej ilości miejsca dostępnej dla aplikacji.
Użyj szerokości, która jest faktycznie używana przez komponent do renderowania. Dostępne są 2 opcje uzyskania tej szerokości:
Jeśli chcesz zmienić gdzie lub jak wyświetlane są treści, użyj kolekcji modyfikatorów lub niestandardowego układu, aby układ był responsywny. Może to być tak proste, jak wypełnienie przez dziecko całej dostępnej przestrzeni lub rozmieszczenie elementów w kilku kolumnach, jeśli jest wystarczająco dużo miejsca.
Jeśli chcesz zmienić co wyświetlasz, użyj
BoxWithConstraints
jako bardziej zaawansowanej alternatywy.BoxWithConstraints
udostępnia ograniczenia pomiarowe, których można używać do wywoływania różnych komponentów na podstawie dostępnej przestrzeni wyświetlania. Ma to jednak swoje minusy, ponieważBoxWithConstraints
odkłada kompozycję do fazy układu, gdy te ograniczenia są znane, co powoduje, że podczas układu trzeba wykonać więcej pracy.
@Composable fun Card(/* ... */) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(/* ... */) Title(/* ... */) } } else { Row { Column { Title(/* ... */) Description(/* ... */) } Image(/* ... */) } } } }
Upewnij się, że wszystkie dane są dostępne w różnych rozmiarach wyświetlacza
Podczas wdrażania komponentu, który korzysta z dodatkowej przestrzeni wyświetlania, możesz mieć pokusę, aby efektywnie wczytywać dane jako efekt uboczny bieżącego rozmiaru wyświetlania.
Jest to jednak sprzeczne z zasadą jednokierunkowego przepływu danych, w której dane mogą być przenoszone i przekazywane do komponentów w celu odpowiedniego renderowania. Do komponentu należy dostarczyć wystarczającą ilość danych, aby zawsze miał wystarczającą ilość treści dla dowolnego rozmiaru wyświetlacza, nawet jeśli część treści może nie być zawsze 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 przypadku przykładu Card
description
jest zawsze przekazywane do Card
. Chociaż element description
jest używany tylko wtedy, gdy szerokość pozwala na jego wyświetlenie, element Card
zawsze wymaga elementu description
, niezależnie od dostępnej szerokości.
Przekazywanie wystarczającej ilości treści upraszcza tworzenie układów dostosowanych do wyświetlacza, ponieważ zmniejsza ich stan i zapobiega skutkom ubocznym podczas przełączania rozmiarów wyświetlacza (co może nastąpić w wyniku zmiany rozmiaru okna, orientacji lub złożenia i rozłożenia urządzenia).
Ta zasada umożliwia też zachowanie stanu podczas zmian układu. Dzięki przenoszeniu informacji, których nie można używać w wszystkich rozmiarach wyświetlania, możesz zachować stan aplikacji podczas zmiany rozmiaru układu. Możesz na przykład użyć flagi logicznej showMore
, aby stan aplikacji był zachowany, 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 dopasowywaniu układów w Compose znajdziesz w tych materiałach:
Przykładowe aplikacje
- CanonicalLayouts to repozytorium sprawdzonych schematów projektowania, które zapewniają optymalne wrażenia użytkowników na dużych wyświetlaczach.
- JetNews pokazuje, jak zaprojektować aplikację, która dostosowuje interfejs użytkownika do dostępnej przestrzeni wyświetlania.
- Reply to elastyczna próbka obsługująca telefony, tablety i urządzenia składane.
- Nowa aplikacja na Androida wykorzystuje układy adaptacyjne do obsługi różnych rozmiarów wyświetlaczy.
Filmy
- Tworzenie interfejsu użytkownika na Androida na dowolny rozmiar ekranu
- Formaty | Android Dev Summit 2022