Obsługa różnych gęstości pikseli

Urządzenia z Androidem mają nie tylko różne rozmiary ekranów – telefony, tablety, telewizory, itp., ale mają też ekrany o różnych rozmiarach. Jeden urządzenie może mieć 160 pikseli na cal, a inne urządzenie może mieć rozmiar 480 pikseli. pikseli w tej samej przestrzeni. Jeśli nie weźmiesz pod uwagę tych różnic w gęstość pikseli, układ może się skalować w wyniku czego obrazy są rozmyte lub mogą wyświetlają się w niewłaściwym rozmiarze.

Na tej stronie dowiesz się, jak zaprojektować aplikację, aby różne gęstości pikseli dzięki zastosowaniu jednostek miary niezależnych od rozdzielczości. i dostarczanie alternatywnych zasobów mapy bitowej dla każdej gęstości pikseli.

Aby dowiedzieć się więcej o tych technikach, obejrzyj film poniżej.

Więcej informacji o projektowaniu zasobów ikon znajdziesz w wytycznych dotyczących ikon interfejsu Material Design.

Użyj pikseli niezależnych od gęstości

Unikaj używania pikseli do definiowania odległości i rozmiarów. Definiowanie wymiarów za pomocą to problem, ponieważ różne ekrany mają różną gęstość pikseli, więc ta sama liczba pikseli odpowiada różnym rozmiarom fizycznym na różnych urządzeniach.

Obraz przedstawiający 2 przykładowe wyświetlacze urządzeń o różnej gęstości
Rys. 1 2 ekrany o tym samym rozmiarze mogą mieć różną liczbę pikseli.

Aby zachować widoczny rozmiar UI na ekranach o różnej gęstości, zaprojektuj UI piksele niezależne od gęstości (dp). Jeden dp to wirtualnej jednostki pikseli, która jest mniej więcej równa 1 pikselowi na ekranie o średniej gęstości. (160 dpi lub gęstość „odniesienia”). Android przekłada tę wartość na odpowiednią liczbę rzeczywistych pikseli dla danej gęstości.

Przyjrzyjmy się dwóm urządzeniom z ilustracji 1. Widok, który Szerokość 100 pikseli po lewej stronie jest dużo większa. Widok Zgodnie z definicjami szerokości 100 dp rozmiar jest taki sam na obu ekranach.

Aby zdefiniować rozmiary tekstu, możesz zamiast tego użyć funkcji skalowalność pikseli (sp) jako jednostek. Jednostka sp to domyślnie ma taki sam rozmiar jak dp, ale zmienia rozmiar w zależności od preferencji użytkownika rozmiar tekstu. Nigdy nie używaj sp dla rozmiarów układów.

Aby na przykład określić odstępy między 2 widokami, użyj dp:

<Button android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/clickme"
    android:layout_marginTop="20dp" />

Określając rozmiar tekstu, użyj sp:

<TextView android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="20sp" />

Przelicz jednostki dp na jednostki pikseli

W niektórych przypadkach należy podawać wymiary w dp, a następnie i przekonwertować je na piksele. Konwersja jednostek dp na piksele ekranu jest taki:

px = dp * (dpi / 160)

Uwaga: nigdy nie koduj na stałe tego równania do obliczania pikseli. Zamiast tego użyj TypedValue.applyDimension(), który konwertuje różne typy wymiarów (dp, sp itp.) na piksele.

Wyobraź sobie aplikację, w której rozpoznawany jest gest przewijania lub przesuwania po przesunięciu palca użytkownika o co najmniej 16 pikseli. Na poziomie bazowym ekranu, palec użytkownika musi przesunąć się o 16 pixels / 160 dpi, co odpowiada 1/10 cala (lub 2,5 mm), przed gest zostanie rozpoznany.

Na urządzeniu na wyświetlaczu o dużej gęstości (240 dpi) musi się ruszać palec użytkownika 16 pixels / 240 dpi, która to 1/15 cala (lub 1,7 mm). Odległość jest znacznie mniejsza, sprawia, że aplikacja wydaje się bardziej wrażliwa dla użytkownika.

Aby rozwiązać ten problem, określ próg gestów w kodzie w dp i a potem przekonwertuj je na piksele. Na przykład:

Kotlin

// The gesture threshold expressed in dp
private const val GESTURE_THRESHOLD_DP = 16.0f

private var gestureThreshold: Int = 0

// Convert the dps to pixels, based on density scale
gestureThreshold = TypedValue.applyDimension(
  COMPLEX_UNIT_DIP,
  GESTURE_THRESHOLD_DP + 0.5f,
  resources.displayMetrics).toInt()

// Use gestureThreshold as a distance in pixels...

Java

// The gesture threshold expressed in dp
private final float GESTURE_THRESHOLD_DP = 16.0f;

// Convert the dps to pixels, based on density scale
int gestureThreshold = (int) TypedValue.applyDimension(
  COMPLEX_UNIT_DIP,
  GESTURE_THRESHOLD_DP + 0.5f,
  getResources().getDisplayMetrics());

// Use gestureThreshold as a distance in pixels...

Pole DisplayMetrics.density określa współczynnik skali używany do przeliczania jednostek dp na pikseli zgodnie z bieżącą gęstością pikseli. Na ekranie o średniej gęstości DisplayMetrics.density równa się 1,0, a na ekranie o dużej gęstości ma on wartość 1,5. Na ekranie o bardzo dużej gęstości jest to 2,0, a na ekranie o małej gęstości – 0,75. Ta wartość jest używane przez TypedValue.applyDimension() do podaj rzeczywistą liczbę pikseli na bieżącym ekranie.

Użyj wstępnie przeskalowanych wartości konfiguracji

Za pomocą klasy ViewConfiguration możesz uzyskać dostęp do typowych odległości, prędkości i czasu używane przez system Android. Na przykład plik odległość w pikselach używana przez platformę służącą do uzyskania progu przewijania z użytkownikiem getScaledTouchSlop():

Kotlin

private val GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).scaledTouchSlop

Java

private final int GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).getScaledTouchSlop();

Metody w funkcji ViewConfiguration zaczynające się od prefiksu getScaled zwrócą wartość w pikselach, która będzie wyświetlana poprawnie niezależnie od gęstości pikseli.

Preferuj grafikę wektorową

Alternatywą dla tworzenia różnych wersji obrazu dostosowanych do różnych gęstości jest utworzyć tylko jedną grafikę wektorową. Grafika wektorowa tworzy obraz za pomocą formatu XML, definiowania ścieżek i kolorów, zamiast używać pikselowych map bitowych. W ten sposób grafikę można skalować do dowolnego rozmiaru bez skalowania artefaktów. najlepiej w przypadku ilustracji, takich jak ikony, a nie fotografie.

Obrazy wektorowe są często przesyłane jako pliki SVG (Scalable Vector Graphics), Android nie obsługuje tego formatu, więc musisz przekonwertować pliki SVG na Wektor Androida rysowalny.

Plik SVG możesz przekonwertować na obiekt rysowalny wektorowy za pomocą biblioteki Vector Asset Studio, w następujący sposób:

  1. W oknie Projekt kliknij prawym przyciskiem myszy katalog res i wybierz Nowe > Zasób wektorowy.
  2. Wybierz Plik lokalny (SVG, PSD).
  3. Znajdź plik, który chcesz zaimportować, i wprowadź zmiany.

    Obraz pokazujący, jak importować pliki SVG w Android Studio
    Rysunek 2. Importowanie pliku SVG z Android Studio.

    W oknie Asset Studio mogą pojawić się błędy co oznacza, że obiekty rysowalne wektorowe nie obsługują niektórych właściwości pliku. Mimo to nadal nie będzie można zaimportować pliku. nieobsługiwane właściwości są ignorowane.

  4. Kliknij Dalej.

  5. Na następnym ekranie potwierdź zbiór źródłowy, w którym chcesz umieścić plik w projekcie. i kliknij Zakończ.

    W przypadku wszystkich gęstości pikseli, więc ten plik zostanie przeniesiony do domyślnego katalogu z elementami rysowania, jak w poniższym przykładzie: w hierarchii. Nie musisz używać katalogów dostosowanych do gęstości.

    res/
      drawable/
        ic_android_launcher.xml
    

Więcej informacji o tworzeniu grafiki wektorowej znajdziesz w artykule o rysowaniu wektorowym. dokumentacji.

Udostępnij alternatywne mapy bitowe

Aby zapewnić dobrą jakość grafiki na urządzeniach o różnej gęstości pikseli, udostępnij wiele wersji każdej bitmapy w aplikacji – po jednej dla każdej zasobnika o odpowiedniej rozdzielczości. W przeciwnym razie Android musi się skalować. bitmapa, tak aby zajmowała tę samą widoczną przestrzeń na każdym ekranie, np. rozmycia.

Obraz pokazujący względne rozmiary map bitowych o różnych rozmiarach gęstości
Rysunek 3. Rozmiary względne dla map bitowych w zasobnikach o różnej gęstości.

W aplikacjach dostępnych jest kilka zasobników gęstości. Tabela 1 opisuje różne dostępne kwalifikatory konfiguracji i typy ekranów. których dotyczą.

Tabela 1. Kwalifikatory konfiguracji dla różnych gęstości pikseli.

Kwalifikator gęstości Opis
ldpi Zasoby na temat ekranów o niskiej gęstości (ldpi) (ok. 120 dpi).
mdpi Zasoby dla ekranów o średniej gęstości (mdpi) (ok. 160 dpi). To jest wartość bazowa gęstość danych.
hdpi Zasoby dotyczące ekranów o dużej gęstości (hdpi) (ok. 240 dpi).
xhdpi Zasoby dotyczące ekranów o bardzo dużej gęstości (xhdpi) (ok. 320 dpi).
xxhdpi Zasoby dotyczące ekranów o bardzo dużej gęstości (xxhdpi) (ok. 480 dpi).
xxxhdpi Zasoby dotyczące bardzo dużej gęstości (xxxhdpi) (ok. 640 dpi).
nodpi Zasoby dla wszystkich gęstości. Są to zasoby niezależne od gęstości. System nie chce skalować zasoby otagowane tym kwalifikatorem niezależnie od gęstości bieżącego ekranu.
tvdpi zasoby dotyczące ekranów o rozdzielczości od mdpi do hdpi; około Ok. 213 dpi. Nie jest uważane za „główne” grupy gęstości. Przeznaczona jest ona głównie dla telewizorów, a większość aplikacji go nie potrzebuje – zapewnia mdpi i hdpi zasobów wystarcza na większość aplikacji, a system skaluje je odpowiednie. Jeśli uznasz, że musisz udostępnić zasoby tvdpi, ustalając ich współczynnik proporcji 1,33 * mdpi. Na przykład obraz o wymiarach 100 x 100 pikseli dla Ekrany mdpi mają rozdzielczość 133 x 133 piksele w przypadku rozdzielczości tvdpi.

Aby utworzyć alternatywne obiekty rysunkowe bitmapy dla różnych gęstości, postępuj zgodnie z instrukcjami Współczynnik skalowania 3:4:6:8:12:16 między 6 podstawowymi gęstościami. Jeśli na przykład masz bitmapa rysowalna o wymiarach 48 x 48 pikseli dla ekranów o średniej gęstości. Rozmiary są następujące:

  • 36 x 36 (0,75x) dla małej gęstości (ldpi)
  • 48 x 48 (1,0x – poziom odniesienia) dla średniej gęstości (mdpi)
  • 72 x 72 (1,5x) – wysoka gęstość (hdpi)
  • 96 x 96 (2,0x) – bardzo duża gęstość (xhdpi)
  • 144 x 144 (3,0x) – bardzo duża gęstość (xxhdpi)
  • 192 x 192 (4,0x) – bardzo duża gęstość (xxxhdpi)

Umieść wygenerowane pliki obrazów w odpowiednim podkatalogu. poniżej res/:

res/
  drawable-xxxhdpi/
    awesome_image.png
  drawable-xxhdpi/
    awesome_image.png
  drawable-xhdpi/
    awesome_image.png
  drawable-hdpi/
    awesome_image.png
  drawable-mdpi/
    awesome_image.png

Za każdym razem, gdy odwołasz się do @drawable/awesomeimage, system wybiera odpowiednią bitmapę, kierując się rozdzielczością dpi ekranu. Jeśli jeśli nie zostaną udostępnione zasoby związane z daną gęstością, system wskaże które najlepiej pasują do reklamy i dopasowują ją do ekranu.

Wskazówka: jeśli masz zasoby, które można przeciągać których nie chcesz skalować, np. podczas wykonywania samodzielnie wprowadzić poprawki do obrazu w czasie działania, a następnie umieścić je katalogu z kwalifikatorem konfiguracji nodpi. Zasoby z tym kwalifikatorem są uważane za niezależne od gęstości, system ich nie skaluje.

Więcej informacji o innych kwalifikatorach konfiguracji jak Android wybiera odpowiednie zasoby bieżącej konfiguracji ekranu, zapoznaj się z omówieniem zasobów aplikacji.

Umieść ikony aplikacji w katalogach mipmap

Podobnie jak w przypadku innych zasobów mapy bitowej musisz udostępnić wersje mapy o określonej gęstości ikonę aplikacji. Jednak niektóre Menu z aplikacjami wyświetlają ikonę aplikacji nawet w 25% większy niż jest dozwolony przez segment gęstości urządzenia.

Na przykład: jeśli zasobnik o gęstości ekranu na urządzeniu to xxhdpi, a największa ikona aplikacji należy podać w języku drawable-xxhdpi, menu z aplikacjami skaluje tę ikonę w górę, przez co wydaje się mniej wyrazisty.

Aby tego uniknąć, umieść wszystkie ikony aplikacji w katalogach mipmap zamiast w katalogach drawable. Nie podoba mi się drawable, wszystkie katalogi mipmap zostaną zachowane w pliku APK, nawet jeśli tworzysz pliki APK dla określonej gęstości. Pozwoli to aplikacjom uruchamiającym wybrać najlepsze ikonę rozdzielczości, która ma być wyświetlana na ekranie głównym.

res/
  mipmap-xxxhdpi/
    launcher_icon.png
  mipmap-xxhdpi/
    launcher_icon.png
  mipmap-xhdpi/
    launcher_icon.png
  mipmap-hdpi/
    launcher_icon.png
  mipmap-mdpi/
    launcher_icon.png

W poprzednim przykładzie urządzenia xxhdpi możesz podać ikonę programu uruchamiającego o większej gęstości w katalogu mipmap-xxxhdpi.

Wskazówki dotyczące projektowania ikon znajdziesz w artykule Ikony systemowe.

Więcej informacji o tworzeniu ikon aplikacji znajdziesz w artykule Tworzenie ikon aplikacji za pomocą Image Asset Studio.

Porady dotyczące rzadkich problemów z gęstością

W tej sekcji opisaliśmy, jak Android przeprowadza skalowanie na potrzeby map bitowych dla różnej gęstości pikseli i sposobu bitmapy są rysowane w różnych gęstościach. Chyba że aplikacja modyfikuje grafikę lub występują problemy z działaniem z różną gęstością pikseli, mogą zignorować tę sekcję.

Aby lepiej zrozumieć, jak obsługiwać różne gęstości podczas manipulowania grafiką musisz wiedzieć, jak system pomaga zapewnić odpowiednią skalę map bitowych. Odbywa się to na kilka sposobów:

  1. Wstępne skalowanie zasobów, takich jak obiekty rysunkowe mapy bitowej

    W zależności od gęstości ekranu system wykorzystuje z aplikacji. Jeśli zasoby nie są dostępne w nad prawidłową gęstością, system wczytuje zasoby domyślne i skaluje je odpowiednio w górę lub w dół. System zakłada, że zasoby domyślne (pochodzące z bez kwalifikatorów konfiguracji) są przeznaczone dla punktu odniesienia gęstości pikseli (mdpi) i dopasowuje rozmiar tych map bitowych do rozmiaru do aktualnej gęstości pikseli.

    Jeśli zażądasz wymiarów wstępnie przeskalowanego zasobu, system zwróci wartości reprezentujące wymiary po skalowaniu. Na przykład bitmapa o wymiarach 50 x 50 pikseli. dla ekranu mdpi jest skalowana do 75x75 pikseli na ekranie hdpi (jeśli nie ma alternatywnego zasobu w przypadku rozdzielczości hdpi), a system raportuje ten rozmiar w ten sposób.

    W niektórych sytuacjach można nie chcieć, aby Android w zasobach. Najłatwiejszym sposobem uniknięcia wstępnego skalowania jest umieszczenie zasobu w katalogu zasobów z kwalifikatorem konfiguracji nodpi. Na przykład:

    res/drawable-nodpi/icon.png

    Gdy system używa mapy bitowej icon.png z tego folderu, nie skaluje jej na podstawie obecnej gęstości urządzeń.

  2. Autoskalowanie wymiarów i współrzędnych w pikselach

    Możesz wyłączyć wymiary i obrazy wstępnego skalowania, ustawiając opcję android:anyDensity na "false" w pliku manifestu lub automatycznie dla Bitmap przez ustawienie inScaled na "false". W W tym przypadku system automatycznie skaluje wszystkie bezwzględne współrzędne pikseli oraz wartości wymiarów w momencie rysowania. Ma to na celu zapewnienie, że piksele zostaną elementy ekranu są nadal wyświetlane przy mniej więcej takim samym rozmiarze fizycznym że można je wyświetlać z wyjściową gęstością pikseli (mdpi). System obsługuje skaluje się w sposób przejrzysty do aplikacji i zgłasza skalowany piksel, z wymiarami aplikacji, a nie z wymiarami fizycznymi w pikselach.

    Załóżmy na przykład, że urządzenie ma ekran o dużej gęstości WVGA, który ma wymiary 480 x 800 pikseli i jest taki sam jak tradycyjny ekran HVGA, ale działa na nim aplikacja z wyłączoną funkcją przez skalowanie. W takim przypadku system „kłamuje” do aplikacji, gdy wysyła zapytanie o ekran i raportuje wymiary 320 x 533, czyli przybliżone przesunięcie mdpi dla gęstości pikseli.

    Następnie, gdy aplikacja wykonuje operacje rysowania, takie jak unieważnianie prostokątów od (10,10) do (100, 100), system przekształca współrzędne, skalując je do odpowiedniej wielkości i w ten sposób unieważnia region (15,15) do (150, 150). Ta rozbieżność może powodować nieoczekiwane działanie, jeśli aplikacja bezpośrednio używa skalowanej mapy bitowej, ale uznaje się to za uzasadnione w celu zapewnienia optymalnej wydajności aplikacji. Jeśli natrafisz na znajduje się w artykule Konwertowanie jednostek dp na piksele .

    Zwykle nie wyłącza się wstępnego skalowania. Najlepszy sposób obsługi wielu z zastosowaniem podstawowych metod opisanych na tej stronie.

czy aplikacja modyfikuje mapy bitowe lub bezpośrednio wchodzi w interakcję z pikselami na ekranie. w inny sposób, może być konieczne wykonanie dodatkowych czynności w celu obsługi gęstości pikseli. Jeśli na przykład reagujesz na gesty dotykowe, zliczając liczby pikseli, które krzyżuje palce, użyj odpowiedniego niezależnych od gęstości pikseli, a nie rzeczywistej liczby pikseli, ale możesz przelicz na wartość dp lub px.

Przetestuj na wszystkich gęstości pikseli

Testowanie aplikacji na wielu urządzeniach z użyciem różnych pikseli gęstości, aby zapewnić prawidłowe skalowanie interfejsu użytkownika. Testowanie na: urządzenia, gdy jest to możliwe; należy korzystać z systemu Android Emulator, jeśli nie masz dostępu do fizycznej dla różnych gęstości pikseli.

Jeśli chcesz przeprowadzać testy na urządzeniach fizycznych, i nie chcesz ich kupować, możesz skorzystać z Laboratorium Firebase, aby dostępu do urządzeń w centrum danych Google.