Tworząc aplikacje na urządzenia składane, programiści często napotykają wyjątkowe trudności, zwłaszcza w przypadku urządzeń takich jak Samsung Trifold czy oryginalny Pixel Fold, które otwierają się w orientacji poziomej (rotation_0 = landscape). Błędy popełniane przez programistów:
- Nieprawidłowe założenia dotyczące orientacji urządzenia.
- Przeoczone przypadki użycia.
- Niepowodzenie w ponownym obliczeniu lub zapisaniu w pamięci podręcznej wartości po zmianach konfiguracji.
Problemy związane z konkretnymi urządzeniami:
- Niezgodność naturalnej orientacji urządzenia między ekranem zewnętrznym a wewnętrznym (założenia oparte na rotation_0 = portrait), co powoduje awarie aplikacji podczas składania i rozkładania urządzenia.
- Różne gęstości ekranu i nieprawidłowa obsługa zmian konfiguracji density.
- Problemy z podglądem z aparatu spowodowane zależnością czujnika aparatu od naturalnej orientacji.
Aby zapewnić użytkownikom wysoką jakość korzystania z urządzeń składanych, skup się na tych najważniejszych obszarach:
- Określ orientację aplikacji na podstawie rzeczywistego obszaru ekranu, który zajmuje aplikacja, a nie fizycznej orientacji urządzenia.
- Aktualizuj podglądy z aparatu, aby prawidłowo zarządzać orientacją urządzenia i formatem obrazu, unikać podglądów wyświetlanych bokiem oraz zapobiegać rozciąganiu lub przycinaniu obrazów.
- Zachowaj ciągłość aplikacji podczas składania lub rozkładania urządzenia, zachowując stan za pomocą
ViewModellub podobnych metod albo ręcznie obsługując zmiany gęstości ekranu i orientacji, co pozwala uniknąć ponownego uruchomienia aplikacji lub utraty stanu. - W przypadku aplikacji korzystających z czujników ruchu dostosuj układ współrzędnych, aby był zgodny z bieżącą orientacją ekranu, i unikaj założeń opartych na rotation_0 = portrait, co gwarantuje precyzyjne interakcje użytkownika.
Tworzenie aplikacji adaptacyjnych
Jeśli Twoja aplikacja jest już adaptacyjna i spełnia wymagania poziomu zoptymalizowanego (poziom 2) określone we wskazówkach dotyczących jakości aplikacji adaptacyjnych, powinna dobrze działać na urządzeniach składanych. W przeciwnym razie, zanim sprawdzisz szczegóły dotyczące urządzeń składanych w 3 miejscach i urządzeń składanych w orientacji poziomej, zapoznaj się z tymi podstawowymi koncepcjami tworzenia aplikacji adaptacyjnych na Androida.
Układy adaptacyjne
Interfejs użytkownika musi obsługiwać nie tylko różne rozmiary ekranu, ale też zmiany formatu obrazu w czasie rzeczywistym, takie jak rozkładanie urządzenia i przechodzenie do trybu wielu okien lub trybu okien na pulpicie. Więcej informacji o tym, jak:
- projektować i wdrażać układy adaptacyjne;
- dostosowywać główną nawigację aplikacji do rozmiaru okna;
- używać klas rozmiaru okna do dostosowywania interfejsu aplikacji;
- upraszczać wdrażanie układów kanonicznych, takich jak lista – szczegóły, za pomocą interfejsów Jetpack API.
Klasy rozmiaru okna
Urządzenia składane, w tym urządzenia składane w orientacji poziomej i urządzenia składane w 3 miejscach, mogą natychmiast przełączać się między klasami rozmiaru okna: kompaktową, średnią i rozszerzoną. Zrozumienie i wdrożenie tych klas zapewnia, że aplikacja wyświetla prawidłowe komponenty nawigacyjne i gęstość treści w zależności od bieżącego stanu urządzenia.
W tym przykładzie używamy biblioteki adaptacyjnej Material 3, aby określić, ile
miejsca ma aplikacja. Najpierw wywołujemy funkcję
currentWindowAdaptiveInfoV2(), a potem używamy odpowiednich
układów dla klas rozmiaru okna:
val adaptiveInfo = currentWindowAdaptiveInfoV2()
val windowSizeClass = adaptiveInfo.windowSizeClass
when {
windowSizeClass.isWidthAtLeastBreakpoint(
WIDTH_DP_EXPANDED_LOWER_BOUND
) -> // Expanded
windowSizeClass.isWidthAtLeastBreakpoint(
WIDTH_DP_MEDIUM_LOWER_BOUND
) -> // Medium
else -> // Compact
}
Więcej informacji znajdziesz w artykule Używanie klas rozmiaru okna.
Jakość aplikacji adaptacyjnych
Przestrzeganie poziomu 2 (aplikacja adaptacyjna zoptymalizowana) lub poziomu 1 (aplikacja adaptacyjna zróżnicowana) we wskazówkach dotyczących jakości aplikacji adaptacyjnych zapewnia, że Twoja aplikacja będzie zapewniać użytkownikom atrakcyjne wrażenia na urządzeniach składanych w 3 miejscach, urządzeniach składanych w orientacji poziomej i innych urządzeniach z dużym ekranem. Wskazówki obejmują najważniejsze kontrole na różnych poziomach, które pozwalają przejść od aplikacji adaptacyjnej do aplikacji zróżnicowanej.
Android 16 lub nowszy
W przypadku aplikacji kierowanych na Androida 16 (interfejs API na poziomie 36) lub nowszego system ignoruje ograniczenia dotyczące orientacji, możliwości zmiany rozmiaru i formatu obrazu na wyświetlaczach o najmniejszej szerokości >= 600 dp. Aplikacje wypełniają całe okno wyświetlacza, niezależnie od formatu obrazu czy preferowanej orientacji użytkownika, a tryb zgodności z czarnymi paskami nie jest już używany.
Specjalne uwagi
Urządzenia składane w 3 miejscach i urządzenia składane w orientacji poziomej mają unikalne zachowania sprzętowe, które wymagają specjalnej obsługi, zwłaszcza w przypadku czujników, podglądu z aparatu i ciągłości konfiguracji (zachowywania stanu podczas składania, rozkładania lub zmiany rozmiaru).
Podgląd z aparatu
Częstym problemem na urządzeniach składanych w orientacji poziomej lub w przypadku obliczeń formatu obrazu (w scenariuszach takich jak tryb wielu okien, tryb okien na pulpicie lub podłączone wyświetlacze) jest to, że podgląd z aparatu jest rozciągnięty, wyświetlany bokiem, przycięty lub obrócony.
Niezgodne założenia
Ten problem często występuje na urządzeniach z dużym ekranem i urządzeniach składanych, ponieważ aplikacje mogą zakładać stałe relacje między funkcjami aparatu (takimi jak format obrazu i orientacja czujnika) a funkcjami urządzenia (takimi jak orientacja urządzenia i naturalna orientacja).
Nowe formaty podważają to założenie. Urządzenie składane może zmieniać rozmiar wyświetlacza i format obrazu bez zmiany orientacji urządzenia. Na przykład rozłożenie urządzenia zmienia format obrazu, ale jeśli użytkownik nie obróci urządzenia, jego orientacja pozostanie taka sama. Jeśli aplikacja zakłada, że format obrazu jest powiązany z orientacją urządzenia, może nieprawidłowo obracać lub skalować podgląd z aparatu. To samo może się zdarzyć, jeśli aplikacja zakłada, że orientacja czujnika aparatu jest zgodna z orientacją pionową urządzenia, co nie zawsze jest prawdą w przypadku urządzeń składanych w orientacji poziomej.
Rozwiązanie 1. Jetpack CameraX (najlepsze)
Najprostszym i najbardziej niezawodnym rozwiązaniem jest użycie biblioteki Jetpack CameraX. Element interfejsu
PreviewView został zaprojektowany tak, aby automatycznie obsługiwać wszystkie złożone aspekty podglądu:
PreviewViewprawidłowo dostosowuje się do orientacji czujnika, orientacji urządzenia i skalowania.- Zachowuje format obrazu z aparatu, zwykle przez wyśrodkowanie i przycięcie (FILL_CENTER).
- W razie potrzeby możesz ustawić typ skalowania na
FIT_CENTER, aby dodać czarne paski do podglądu.
Więcej informacji znajdziesz w dokumentacji CameraX w artykule Implementowanie podglądu.
Rozwiązanie 2. CameraViewfinder
Jeśli używasz istniejącej bazy kodu Camera2, biblioteka CameraViewfinder (zgodna wstecznie z interfejsem API na poziomie 21) jest kolejnym nowoczesnym rozwiązaniem. Upraszcza wyświetlanie przekazu z kamery za pomocą TextureView lub SurfaceView i stosuje wszystkie niezbędne przekształcenia (format obrazu, skalowanie i obrót).
Więcej informacji znajdziesz w poście na blogu Introducing Camera Viewfinder i w przewodniku dla programistów dotyczącym podglądu z aparatu.
Rozwiązanie 3. Ręczne wdrożenie Camera2
Jeśli nie możesz używać CameraX ani CameraViewfinder, musisz ręcznie obliczyć orientację i format obrazu oraz zadbać o to, aby obliczenia były aktualizowane przy każdej zmianie konfiguracji:
- Pobierz orientację czujnika aparatu (np. 0, 90, 180, 270 stopni) z
CameraCharacteristics. - Pobierz bieżącą orientację wyświetlacza urządzenia (np. 0, 90, 180, 270 stopni).
- Użyj tych 2 wartości, aby określić niezbędne przekształcenia dla
SurfaceViewlubTextureView. - Aby uniknąć zniekształceń, upewnij się, że format obrazu wyjściowego
Surfacejest zgodny z formatem obrazu podglądu z aparatu. - Aplikacja aparatu może działać w części ekranu, w trybie wielu okien, trybie okien na pulpicie lub na podłączonym wyświetlaczu. Z tego powodu do określania wymiarów wizjera aparatu nie należy używać rozmiaru ekranu, tylko danych okna.
Więcej informacji znajdziesz w przewodniku dla programistów dotyczącym podglądu z aparatu i w filmie Your Camera app on different form factors.
Rozwiązanie 4. Wykonywanie podstawowych działań aparatu za pomocą intencji
Jeśli nie potrzebujesz wielu funkcji aparatu, prostym rozwiązaniem jest wykonywanie podstawowych działań aparatu, takich jak robienie zdjęć lub nagrywanie filmów, za pomocą domyślnej aplikacji aparatu na urządzeniu. Nie musisz integrować się z biblioteką aparatu – zamiast tego użyj intencji.
Więcej informacji znajdziesz w artykule Intencje aparatu.
Konfiguracja i ciągłość
Urządzenia składane zwiększają wszechstronność interfejsu, ale mogą inicjować więcej zmian konfiguracji niż urządzenia nieskładane. Twoja aplikacja musi zarządzać tymi zmianami konfiguracji i ich kombinacjami, takimi jak orientacja urządzenia, składanie/rozkładanie i zmiana rozmiaru okna w trybie wielu okien lub trybie okien na pulpicie, przy jednoczesnym zachowaniu lub przywracaniu stanu aplikacji. Na przykład aplikacje muszą zachowywać następującą ciągłość:
- Stan aplikacji bez awarii lub powodowania zakłócających zmian dla użytkowników (np. podczas przełączania ekranów lub wysyłania aplikacji w tle).
- Pozycja przewijania pól możliwych do przewijania.
- Tekst wpisany w pola tekstowe i stan klawiatury.
- Pozycja odtwarzania multimediów, aby odtwarzanie było wznawiane w miejscu, w którym zostało przerwane po zainicjowaniu zmiany konfiguracji.
Często wywoływane zmiany konfiguracji to screenSize, smallestScreenSize, screenLayout, orientation, density, fontScale, touchscreen i keyboard.
Zobacz android:configChanges i Obsługa zmian konfiguracji. Dodatkowe informacje o zarządzaniu stanem aplikacji znajdziesz w artykule Zapisywanie stanów interfejsu.
Zmiany konfiguracji gęstości
Ekran zewnętrzny i wewnętrzny urządzeń składanych w 3 miejscach i urządzeń składanych w orientacji poziomej mogą mieć różne gęstości pikseli. Dlatego zarządzanie zmianą konfiguracji density wymaga dodatkowej uwagi. Android zwykle ponownie uruchamia aktywność, gdy zmienia się gęstość wyświetlacza, co może spowodować utratę danych. Aby uniemożliwić systemowi ponowne uruchomienie aktywności, zadeklaruj obsługę gęstości w manifeście i zarządzaj zmianą konfiguracji programowo w aplikacji.
Konfiguracja AndroidManifest.xml
density: deklaruje, że aplikacja będzie obsługiwać zmianę gęstości ekranu.- Inne zmiany konfiguracji: warto też zadeklarować inne często występujące zmiany konfiguracji, np.
screenSize,orientation,keyboardHidden,fontScaleitp.
Deklarowanie gęstości (i innych zmian konfiguracji) uniemożliwia systemowi ponowne uruchomienie aktywności i zamiast tego wywołuje onConfigurationChanged().
Implementacja onConfigurationChanged()
Gdy nastąpi zmiana gęstości, musisz zaktualizować zasoby (np. ponownie załadować mapy bitowe lub ponownie obliczyć rozmiary układu) w wywołaniu zwrotnym:
- Sprawdź, czy DPI zmieniło się na
newConfig.densityDpi. - Zresetuj widoki niestandardowe, niestandardowe elementy rysowalne itp. do nowej gęstości.
Elementy zasobów do przetworzenia
- Zasób obrazu: zastąp mapy bitowe i elementy rysowalne zasobami specyficznymi dla gęstości lub bezpośrednio dostosuj skalę.
- Jednostka układu (konwersja dp na px): ponownie oblicz rozmiar widoku, margines, dopełnienie.
- Rozmiar czcionki i tekstu: ponownie zastosuj rozmiar tekstu w jednostkach sp.
- Rysowanie niestandardowego
View/Canvas: zaktualizuj wartości oparte na pikselach używane do rysowaniaCanvas
Określanie orientacji aplikacji
Podczas tworzenia aplikacji adaptacyjnych nigdy nie polegaj na fizycznej orientacji urządzenia, ponieważ będzie ona ignorowana na urządzeniach z dużym ekranem a aplikacja w trybie wielu okien może mieć inną orientację niż urządzenie. Zamiast tego użyj Configuration.orientation lub WindowMetrics, aby określić, czy aplikacja jest obecnie w orientacji poziomej czy pionowej, na podstawie rozmiaru okna.
Rozwiązanie 1. Użyj Configuration.orientation
Ta właściwość określa orientację, w której jest obecnie wyświetlana Twoja aplikacja.
Rozwiązanie 2. Użyj WindowMetrics#getBounds()
Możesz pobrać bieżące granice wyświetlania aplikacji i sprawdzić jej szerokość oraz wysokość, aby określić orientację.
Jeśli chcesz ograniczyć orientację aplikacji na telefonach (lub ekranach zewnętrznych urządzeń składanych), ale nie na urządzeniach z dużym ekranem, przeczytaj artykuł Orientacja aplikacji ograniczona na telefonach, ale nie na dużych ekranach.
Pozycje i tryby wyświetlania
Pozycje i stany urządzeń składanych, takie jak tabletop i HALF_OPENED są
obsługiwane zarówno przez urządzenia składane w orientacji pionowej, jak i poziomej. Urządzenia składane w 3 miejscach nie obsługują jednak pozycji tabletop i nie można ich używać w stanie HALF_OPENED. Urządzenia składane w 3 miejscach oferują natomiast większy ekran, który zapewnia wyjątkowe wrażenia użytkownikom po całkowitym rozłożeniu.
Aby wyróżnić swoją aplikację na urządzeniach składanych, które obsługują HALF_OPENED, użyj interfejsów Compose
Material 3 Adaptive API, takich jak collectFoldingFeaturesAsState() i
currentWindowAdaptiveInfoV2().
Więcej informacji o pozycjach i stanach urządzeń składanych oraz o obsłudze podglądu z aparatu znajdziesz w tych przewodnikach dla programistów:
Urządzenia składane zapewniają wyjątkowe wrażenia podczas oglądania. Tryb wyświetlania na tylnym ekranie i tryb podwójnego ekranu umożliwiają tworzenie specjalnych funkcji wyświetlania na urządzeniach składanych, takich jak podgląd selfie z tylnego aparatu i jednoczesne, ale różne wyświetlanie na ekranach wewnętrznym i zewnętrznym. Więcej informacji znajdziesz w tych artykułach:
Blokowanie orientacji na naturalną orientację czujnika
W bardzo konkretnych przypadkach użycia – zwłaszcza w przypadku aplikacji, które muszą zajmować cały ekran niezależnie od stanu złożenia urządzenia – flaga nosensor pozwala zablokować aplikację w naturalnej orientacji urządzenia. Na przykład w przypadku urządzenia Pixel Fold naturalna orientacja urządzenia po złożeniu to orientacja pionowa, a po rozłożeniu – pozioma. Dodanie flagi nosensor powoduje, że aplikacja jest zablokowana w orientacji pionowej, gdy działa na ekranie zewnętrznym, oraz w orientacji poziomej, gdy działa na ekranie wewnętrznym.
<activity
android:name=".MainActivity"
android:screenOrientation="nosensor">
Gry i ponowne mapowanie czujników XR
W przypadku gier i aplikacji XR surowe dane z czujników (np. z żyroskopu lub akcelerometru) są udostępniane w układzie współrzędnych stałym dla urządzenia. Jeśli użytkownik obróci urządzenie, aby grać w grę w orientacji poziomej, osie czujnika nie będą się obracać wraz z ekranem, co spowoduje nieprawidłowe sterowanie w grze.
Aby rozwiązać ten problem, sprawdź bieżącą wartość Display.getRotation() i odpowiednio zmapuj osie:
- Obrót 0: x=x, y=y
- Obrót 90: x=-y, y=x
- Obrót 180: x=-x, y=-y
- Obrót 270: x=y, y=-x
W przypadku wektorów obrotu (używanych w kompasie lub aplikacjach XR) użyj SensorManager.remapCoordinateSystem(), aby zmapować kierunek obiektywu aparatu lub górną część ekranu na nowe osie na podstawie bieżącego obrotu.
Zgodność aplikacji
Aby zapewnić zgodność aplikacji ze wszystkimi formatami i podłączonymi wyświetlaczami, muszą one być zgodne ze wskazówkami dotyczącymi jakości aplikacji. Jeśli aplikacja nie spełnia tych wskazówek, producenci urządzeń mogą wdrożyć rozwiązania zapewniające zgodność, ale może to pogorszyć wrażenia użytkowników.
Dodatkowe informacje znajdziesz w pełnej liście obejść problemów ze zgodnością udostępnianych na platformie, zwłaszcza tych związanych z podglądem z aparatu, zastąpieniami i zmianami w interfejsie Android 16 API, które mogą zmienić zachowanie aplikacji.
Więcej informacji o tworzeniu aplikacji adaptacyjnych znajdziesz we wskazówkach dotyczących jakości aplikacji adaptacyjnych.