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:
- błędne założenia dotyczące orientacji urządzenia,
- pominięte 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ć wysoką jakość obsługi na urządzeniach 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 formatami obrazu, unikać podglądów wyświetlanych bokiem oraz zapobiegać rozciąganiu lub przycinaniu obrazów.
- Zapewnij ciągłość działania 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 i orientacji ekranu, 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 z 3 ekranami 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, np. podczas rozkładania urządzenia i przechodzenia 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 z 3 ekranami, mogą natychmiast przełączać się między klasami rozmiaru okna: kompaktową, średnią i rozszerzoną. Zrozumienie i wdrożenie tych klas gwarantuje, że aplikacja będzie 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ę
currentWindowAdaptiveInfo(), a potem używamy odpowiednich
układów dla 3 klas rozmiaru okna:
val adaptiveInfo = currentWindowAdaptiveInfo()
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 gwarantuje, że aplikacja będzie zapewniać atrakcyjne wrażenia użytkownikom urządzeń z 3 ekranami, urządzeń składanych w orientacji poziomej i innych urządzeń 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 i nowsze wersje
W przypadku aplikacji kierowanych na Androida 16 (interfejs API na poziomie 36) i nowsze wersje 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 i preferowanej orientacji użytkownika, a tryb zgodności z czarnymi pasami nie jest już używany.
Specjalne uwagi
Urządzenia z 3 ekranami 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 podczas obliczania formatu obrazu (w scenariuszach takich jak tryb wielu okien, tryb okien na pulpicie lub podłączone wyświetlacze) jest rozciągnięty, obrócony, przycięty lub wyświetlany bokiem podgląd z aparatu.
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ą urządzenia w trybie pionowym, 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 pasy 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ęczna implementacja 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, w 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, ale 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. 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ć ciągłość w tych obszarach:
- 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 z możliwością przewijania.
- Tekst wpisany w polach tekstowych 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
Zewnętrzny i wewnętrzny ekran urządzeń z 3 ekranami 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 zapobiec ponownemu uruchamianiu Activity przez system, zadeklaruj obsługę gęstości w pliku manifestu 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) zapobiega ponownemu uruchamianiu aktywności przez system i zamiast tego wywołuje onConfigurationChanged().
Implementacja onConfigurationChanged()
Gdy nastąpi zmiana gęstości, musisz zaktualizować zasoby (np. ponownie wczytać 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 o określonej 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 orientacji urządzenia fizycznego, 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 aplikacja.
Rozwiązanie 2. Użyj WindowMetrics#getBounds()
Możesz pobrać bieżące granice wyświetlacza aplikacji i sprawdzić jej szerokość oraz wysokość, aby określić orientację.
Jeśli chcesz ograniczyć orientację aplikacji na telefonach (lub zewnętrznych ekranach urządzeń składanych), ale nie na urządzeniach z dużym ekranem, zobacz Ograniczanie orientacji aplikacji na telefonach.
Pozycje i tryby wyświetlania
Pozycje i stany urządzeń składanych, takie jak tryb stołu i HALF_OPENED, są obsługiwane zarówno przez urządzenia składane w orientacji pionowej, jak i poziomej. Urządzenia z 3 ekranami nie obsługują jednak trybu stołu i nie można ich używać w stanie HALF_OPENED. Zamiast tego urządzenia z 3 ekranami oferują większy ekran, który zapewnia wyjątkowe wrażenia użytkownikom po pełnym rozłożeniu.
Aby wyróżnić aplikację na urządzeniach składanych, które obsługują HALF_OPENED, użyj interfejsów Jetpack
WindowManager API, takich jak FoldingFeature.
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 oferują 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 umożliwia zablokowanie aplikacji w naturalnej orientacji urządzenia. Na przykład w przypadku Pixela 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ść ze wszystkimi formatami i podłączonymi wyświetlaczami, aplikacje muszą 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żytkownika.
Dodatkowe informacje znajdziesz w obszernej liście obejść problemów ze zgodnością dostępnych na platformie, w szczególności 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.