Elastyczne/adaptacyjne projektowanie z widokami danych

Układy elastyczne i adaptacyjne zapewniają optymalną wygodę użytkowników niezależnie od rozmiaru ekranu. Zaimplementuj układy elastyczne lub adaptacyjne, aby Twoja aplikacja korzystająca z widoków danych obsługiwała wszystkie rozmiary, orientacje i konfiguracje wyświetlacza, w tym konfiguracje z możliwością zmiany rozmiaru, takie jak tryb wielu okien.

Elastyczne projektowanie stron

Pierwszym krokiem do obsługi różnych formatów urządzeń jest stworzenie układu, który będzie reagował na wahania ilości dostępnego miejsca na wyświetlaczu aplikacji.

Układ ograniczeń

Najlepszym sposobem utworzenia układu elastycznego jest użycie ConstraintLayout jako układu podstawowego interfejsu. ConstraintLayout pozwala określić położenie i rozmiar każdego widoku zgodnie z relacjami przestrzennymi z innymi widokami w układzie. Wszystkie widoki można następnie przesuwać i zmieniać razem w miarę zmiany obszaru wyświetlania.

Najłatwiejszym sposobem utworzenia układu za pomocą ConstraintLayout jest użycie Edytora układów w Android Studio. Edytor układów umożliwia przeciąganie nowych widoków do układu, stosowanie ograniczeń względem widoków nadrzędnych i równorzędnych oraz ustawianie właściwości widoków – bez konieczności ręcznego edytowania kodu XML.

Rysunek 3. Edytor układu w Android Studio z widocznym elementem ConstraintLayout.

Więcej informacji znajdziesz w artykule o tworzeniu elastycznego interfejsu z wykorzystaniem ograniczeń.

Elastyczne dostosowanie szerokości i wysokości

Aby mieć pewność, że układ będzie dostosowywany do różnych rozmiarów reklam displayowych, zamiast wartości umieszczonych na stałe użyj do określania szerokości i wysokości komponentów widoku wrap_content, match_parent lub 0dp (match constraint):

  • wrap_content: rozmiar widoku jest ustawiany tak, aby pasował do zawartości widoku.
  • match_parent: widok maksymalnie rozwija się w widoku nadrzędnym.
  • 0dp (match constraint): w ConstraintLayout, podobnie jak match_parent. Widok zajmuje całe dostępne miejsce w ramach ograniczeń widoku.

Na przykład:

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/lorem_ipsum" />

Rysunek 4 pokazuje, jak szerokość i wysokość elementu TextView zmieniają się w zależności od szerokości wyświetlacza i orientacji urządzenia.

Rysunek 4. Elastyczny TextView.

Element TextView ustawia jego szerokość, aby wypełnić całą dostępną przestrzeń (match_parent) i jego wysokość na dokładnie taką wysokość miejsca, jaka jest wymagana przez wysokość zawartego tekstu (wrap_content). Dzięki temu widok może się dostosowywać do różnych wymiarów wyświetlacza i różnych ilości tekstu.

Jeśli używasz LinearLayout, możesz też rozwijać widoki podrzędne zależne od wagi układu, by proporcjonalnie wypełniły dostępne miejsce. Używanie wag w zagnieżdżonym elemencie LinearLayout wymaga jednak od systemu wykonania wielu przejść układu, aby określić rozmiar każdego widoku, co spowalnia działanie interfejsu.

ConstraintLayout może tworzyć prawie wszystkie możliwe układy za pomocą LinearLayout bez wpływu na wydajność, więc przekonwertuj zagnieżdżone elementy LinearLayout na ConstraintLayout. Następnie możesz definiować układy ważone za pomocą łańcuchów ograniczeń.

Projektowanie adaptacyjne

Układ aplikacji powinien zawsze dostosowywać się do różnych rozmiarów displayowych. Jednak nawet układ elastyczny nie może zapewnić użytkownikom najlepszych wrażeń na każdym urządzeniu lub wyświetlaczu w trybie wielu okien. Na przykład interfejs zaprojektowany pod kątem telefonu prawdopodobnie nie zapewnia optymalnej wygody użytkowania na tablecie. Projektowanie adaptacyjne udostępnia alternatywne układy zoptymalizowane pod kątem różnych wymiarów wyświetlania.

Układ panelu przesuwnego w interfejsach ze szczegółami listy

Interfejs ze szczegółami listy zwykle działa inaczej na ekranach o różnych rozmiarach. Na dużych ekranach panele listy i szczegółów zwykle wyświetlają się obok siebie. Po wybraniu elementu z listy informacje o nim są wyświetlane w panelu szczegółów bez zmiany interfejsu – oba panele pozostają obok siebie. Na małych ekranach są one jednak wyświetlane oddzielnie, a każdy z nich zajmuje cały obszar wyświetlania. Po wybraniu elementu w panelu listy panel szczegółów (zawierający informacje o wybranym elemencie) zastąpi panel listy. Nawigacja wsteczna zastępuje panel szczegółów listą.

SlidingPaneLayout zarządza logiką określania, która z dwóch opcji jest odpowiednia dla bieżącego rozmiaru okna:

<?xml version="1.0" encoding="utf-8"?>
<androidx.slidingpanelayout.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="start" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="300dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        app:defaultNavHost="true"
        app:navGraph="@navigation/item_navigation" />

</androidx.slidingpanelayout.widget.SlidingPaneLayout>

Działanie SlidingPaneLayout zależy od atrybutów layout_width i layout_weight w dwóch widokach danych w SlidingPaneLayout. W tym przykładzie, jeśli okno jest wystarczająco duże (co najmniej 580 dp szerokości) do wyświetlenia obu widoków, panele zostaną wyświetlone obok siebie. Jeśli jednak szerokość okna jest mniejsza niż 580 dp, panele przesuwają się jedna po drugiej, zajmujących całe okno aplikacji.

Jeśli szerokość okna jest większa niż łączna określona minimalna szerokość (580 dp), do proporcjonalnego rozmiaru 2 paneli można używać wartości layout_weight. W tym przykładzie panel listy ma zawsze szerokość 280 dp, ponieważ nie ma przypisanej wagi. Panel szczegółów zawsze wypełnia jednak wszystkie obszary poziome przekraczające 580 dp ze względu na ustawienie layout_weight widoku.

Zasoby dotyczące alternatywnego układu

Aby dostosować projekt interfejsu do różnych rozmiarów wyświetlacza, użyj alternatywnych układów identyfikowanych przez kwalifikatory zasobów.

Rysunek 5. Ta sama aplikacja używająca różnych układów dla różnych rozmiarów wyświetlacza.

Możesz zapewnić adaptacyjne układy dostosowane do ekranu, tworząc dodatkowe katalogi res/layout/ w kodzie źródłowym aplikacji. Utwórz katalog dla każdej konfiguracji ekranu, która wymaga innego układu. Następnie do nazwy katalogu layout dodaj kwalifikator konfiguracji ekranu (np. layout-w600dp w przypadku ekranów o dostępnej szerokości 600 dp).

Kwalifikatory konfiguracji reprezentują widoczną przestrzeń do wyświetlania dostępną dla interfejsu aplikacji. Podczas wybierania układu aplikacji system uwzględnia wszystkie dekoracje systemu (np. pasek nawigacyjny) i zmiany konfiguracji okien (np. tryb wielu okien).

Aby dowiedzieć się, jak tworzyć alternatywne układy w Android Studio, przeczytaj sekcję o używaniu wariantów układu do optymalizacji pod kątem różnych ekranów w artykule Tworzenie interfejsu z użyciem widoków.

Kwalifikator o najmniejszej szerokości

Kwalifikator rozmiaru ekranu o najmniejszej szerokości umożliwia podanie alternatywnych układów na potrzeby wyświetlaczy o minimalnej szerokości mierzonej w pikselach niezależnych od gęstości (dp).

Opisując rozmiar ekranu jako miarę dp, Android umożliwia tworzenie układów zaprojektowanych pod kątem konkretnych wymiarów wyświetlacza bez uwzględniania różnych gęstości pikseli.

Możesz np. utworzyć układ o nazwie main_activity zoptymalizowany pod kątem telefonów i tabletów, tworząc różne wersje pliku w różnych katalogach:

res/layout/main_activity.xml           # For phones (smaller than 600dp smallest width)
res/layout-sw600dp/main_activity.xml   # For 7" tablets (600dp wide or wider)

Kwalifikator o mniejszej szerokości określa najmniejszy z dwóch stron wyświetlacza, niezależnie od bieżącej orientacji urządzenia. Umożliwia on określenie ogólnego rozmiaru wyświetlacza dostępnego w przypadku danego układu.

Oto jak inne najmniejsze wartości szerokości odpowiadają typowym rozmiarom ekranu:

  • 320 dp: mały ekran telefonu (240 x 320 ldpi, 320 x 480 mdpi, 480 x 800 hdpi itp.)
  • 480 dp: duży ekran telefonu ~5′′ (480 x 800 mdpi)
  • 600dp: 7" tablet (600x1024 mdpi)
  • 720 dp: 10" tablet (720 x 1280 mdpi, 800 x 1280 mdpi itp.)

Na rysunku poniżej przedstawiono bardziej szczegółowy obraz tego, jak różne szerokości dp odpowiadają różnym rozmiarom i orientacjom ekranu.

Rysunek 6. Zalecana szerokość punktów przerwania w celu obsługi różnych rozmiarów ekranu.

Wartościami kwalifikatora o najmniejszej szerokości są dp, ponieważ liczy się ilość miejsca dostępnego na ekranie, gdy system uwzględnia gęstość pikseli (a nie rozdzielczość nieprzetworzonych pikseli).

Rozmiary określone za pomocą kwalifikatorów zasobów (np. najmniejsza szerokość) nie odpowiadają rozmiarom ekranu. Rozmiary określają szerokość lub wysokość w jednostkach dp, które są dostępne dla okna aplikacji. System Android może używać części ekranu do interfejsu użytkownika (np. paska systemu na dole lub paska stanu u góry), więc część ekranu może być niedostępna w przypadku danego układu. Jeśli aplikacja jest używana w trybie wielu okien, ma dostęp tylko do rozmiaru okna, w którym się znajduje. Zmiana rozmiaru okna powoduje zmianę konfiguracji z nowym rozmiarem okna, dzięki czemu system może wybrać odpowiedni plik układu. Zadeklarowane rozmiary kwalifikatorów zasobów powinny określać tylko ilość miejsca potrzebną przez aplikację. System uwzględnia miejsce wykorzystywane przez interfejs systemu podczas udostępniania układu.

Dostępny kwalifikator szerokości

Zamiast zmieniać układ na podstawie najmniejszej szerokości wyświetlacza, warto zmienić układ na podstawie dostępnych szerokości lub wysokości. Na przykład układ z 2 panelami może być stosowany, gdy ekran ma szerokość co najmniej 600 dp. Ta opcja może się zmieniać w zależności od tego, czy urządzenie ma orientację poziomą czy pionową. W takim przypadku użyj kwalifikatora dostępnej szerokości w ten sposób:

res/layout/main_activity.xml         # For phones (smaller than 600dp available width)
res/layout-w600dp/main_activity.xml  # For 7" tablets or any screen with 600dp available width
                                     # (possibly landscape phones)

Jeśli w przypadku Twojej aplikacji dostępna wysokość jest istotna, możesz użyć kwalifikatora o dostępnej wysokości. Na przykład layout-h600dp w przypadku ekranów o wysokości co najmniej 600 dp.

Kwalifikatory orientacji

Nawet jeśli możesz obsługiwać wszystkie odmiany rozmiaru, używając tylko kombinacji kwalifikatorów najmniejszej szerokości i dostępnej szerokości, warto też zmienić wrażenia użytkownika, gdy przełącza się on między orientacją pionową i poziomą.

Aby to zrobić, do nazw katalogów układu możesz dodać kwalifikatory port lub land. Pamiętaj tylko, aby kwalifikatory orientacji znajdowały się po kwalifikatorach rozmiaru. Na przykład:

res/layout/main_activity.xml                # For phones
res/layout-land/main_activity.xml           # For phones in landscape
res/layout-sw600dp/main_activity.xml        # For 7" tablets
res/layout-sw600dp-land/main_activity.xml   # For 7" tablets in landscape

Więcej informacji o wszystkich kwalifikatorach konfiguracji ekranu znajdziesz w artykule Omówienie zasobów aplikacji.

Klasy rozmiaru okna

Klasy rozmiaru okna to punkty przerwania widocznego obszaru, które pomagają tworzyć układy adaptacyjne. Punkty przerwania określają obszar wyświetlania dostępny dla aplikacji jako kompaktowy, średni lub rozwinięty. Szerokość i wysokość są określane oddzielnie, więc aplikacja zawsze ma klasę rozmiaru okna oznaczającą szerokość i klasę rozmiaru okna dla wysokości.

Aby automatycznie zastosować układy adaptacyjne:

  • Utwórz zasoby układu na podstawie punktów przerwania klasy rozmiaru okna
  • Oblicz klasy szerokości i wysokości okna aplikacji za pomocą funkcji WindowSizeClass#compute() z biblioteki Jetpack WindowManager
  • Zwiększ zasób układu na potrzeby bieżących klas rozmiaru okna

Więcej informacji o klasach rozmiarów okien znajdziesz w artykule Obsługa różnych rozmiarów ekranu.

Modularyzowane komponenty interfejsu użytkownika z użyciem fragmentów

Projektując aplikację na potrzeby wyświetlaczy o różnych rozmiarach, użyj fragmentów, aby wyodrębnić logikę UI do osobnych komponentów, aby nie powielać działania interfejsu w różnych działaniach. Następnie możesz łączyć fragmenty, aby tworzyć układy z kilkoma panelami na dużych ekranach, lub umieszczać je w osobnych działaniach na małych ekranach.

Na przykład można zaimplementować wzorzec szczegółów listy (patrz SlidingPaneLayout powyżej) z jednym fragmentem zawierającym listę i drugim ze szczegółami elementu listy. Na dużych ekranach fragmenty można wyświetlać obok siebie, a na małych ekranach – pojedynczo – wypełniać ekran.

Więcej informacji znajdziesz w omówieniu fragmentów.

Umieszczanie aktywności

Jeśli aplikacja obejmuje wiele aktywności, dzięki umieszczaniu aktywności możesz łatwo stworzyć adaptacyjny interfejs użytkownika.

W oknie zadań aplikacji wyświetla się wiele działań lub wiele wystąpień tej samej aktywności jednocześnie. Na dużych ekranach aktywności można wyświetlać obok siebie, a na małych ekranach – jedna nad drugą.

Aby określić sposób wyświetlania działań w aplikacji, możesz utworzyć plik konfiguracji XML, za pomocą którego system określi odpowiednią prezentację na podstawie rozmiaru wyświetlacza. Możesz też wywoływać interfejs Jetpack WindowManager API.

Wstawianie aktywności umożliwia zmianę orientacji urządzenia, a także urządzeń składanych oraz czynności ich grupowania i cofania podczas obracania, złożenia i rozkładania.

Więcej informacji znajdziesz w artykule Umieszczanie aktywności.

Rozmiary ekranu i proporcje obrazu

Przetestuj aplikację na różnych rozmiarach i formatach obrazu, aby upewnić się, że interfejs użytkownika jest prawidłowo skalowany.

Android 10 (poziom interfejsu API 29) i nowszy obsługują szeroki zakres formatów obrazu. Składane formaty mogą różnić się od wysokich, wąskich ekranów (np. po złożeniu 21:9) po kwadratowy format obrazu 1:1 po rozłożeniu.

Aby zapewnić zgodność z jak największą liczbą urządzeń, przetestuj swoje aplikacje pod kątem możliwie największej liczby z tych formatów ekranu:

Rysunek 7. Różne proporcje ekranu.

Jeśli nie masz dostępu do urządzeń dla wszystkich rozmiarów ekranu, które chcesz przetestować, możesz użyć emulatora Androida do emulacji prawie każdego rozmiaru ekranu.

Jeśli wolisz przeprowadzić testy na prawdziwym urządzeniu, ale nie masz takiego urządzenia, możesz skorzystać z Laboratorium Firebase, by uzyskać dostęp do urządzeń w centrum danych Google.

Dodatkowe materiały