Verschiedene Pixeldichten unterstützen

Android-Geräte haben nicht nur unterschiedliche Bildschirmgrößen, z. B. Mobiltelefone, Tablets oder Fernseher, sondern auch Bildschirme mit verschiedenen Pixelgrößen. Ein Gerät kann 160 Pixel pro Zoll haben, während ein anderes 480 Pixel bei gleichem Platz bietet. Wenn Sie diese Schwankungen der Pixeldichte nicht berücksichtigen, skaliert das System die Bilder eventuell, was verschwommene Bilder zur Folge hat oder die Bilder möglicherweise in der falschen Größe angezeigt werden.

Auf dieser Seite wird beschrieben, wie Sie Ihre App so gestalten können, dass sie unterschiedliche Pixeldichten unterstützt. Dazu verwenden Sie auflösungsunabhängige Maßeinheiten und stellen alternative Bitmap-Ressourcen für jede Pixeldichte bereit.

Sehen Sie sich das folgende Video an, um einen Überblick über diese Techniken zu erhalten.

Weitere Informationen zum Entwerfen von Symbol-Assets finden Sie in den Richtlinien für Material Design-Symbole.

Dichteunabhängige Pixel verwenden

Vermeiden Sie die Verwendung von Pixeln, um Abstände oder Größen zu definieren. Die Definition von Abmessungen mithilfe von Pixeln ist ein Problem, da unterschiedliche Bildschirme unterschiedliche Pixeldichten haben. Daher entspricht die gleiche Anzahl von Pixeln unterschiedlichen physischen Größen auf verschiedenen Geräten.

Ein Bild, auf dem zwei Beispielgeräte mit unterschiedlicher Pixeldichte zu sehen sind
Abbildung 1: Zwei Bildschirme derselben Größe können eine unterschiedliche Anzahl von Pixeln haben.

Um die sichtbare Größe der UI auf Bildschirmen mit unterschiedlicher Dichte beizubehalten, verwenden Sie dichteunabhängige Pixel (dp) als Maßeinheit. Ein dp ist eine virtuelle Pixeleinheit, die auf einem Bildschirm mittlerer Dichte ungefähr gleich einem Pixel entspricht (160 dpi bzw. die „Baseline“-Dichte). Android wandelt diesen Wert in die entsprechende Anzahl an realen Pixeln für die jeweils andere Dichte um.

Betrachte die beiden Geräte in Abbildung 1. Eine Ansicht mit einer Breite von 100 Pixeln erscheint auf dem Gerät links viel größer. Eine Ansicht, die mit einer Breite von 100 dp definiert ist, wird auf beiden Bildschirmen dieselbe Größe angezeigt.

Beim Definieren der Textgrößen können Sie stattdessen skalierbare Pixel (sp) als Einheiten verwenden. Die sp-Einheit hat standardmäßig die gleiche Größe wie ein dp, ihre Größe wird jedoch an die vom Nutzer bevorzugte Textgröße angepasst. Verwenden Sie sp nie für Layoutgrößen.

Um beispielsweise den Abstand zwischen zwei Ansichten festzulegen, verwenden Sie dp:

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

Verwenden Sie sp zur Angabe der Textgröße:

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

dp-Einheiten in Pixeleinheiten umrechnen

In einigen Fällen müssen Sie die Abmessungen in dp angeben und dann in Pixel konvertieren. Die Umwandlung von dp-Einheiten in Bildschirmpixel sieht so aus:

px = dp * (dpi / 160)

Hinweis:Diese Gleichung sollte niemals hartcodiert werden, um Pixel zu berechnen. Verwenden Sie stattdessen TypedValue.applyDimension(), um viele Dimensionstypen (dp, sp usw.) in Pixel umzurechnen.

Stellen Sie sich eine App vor, in der eine Scroll- oder fließ-Geste erkannt wird, nachdem der Finger des Nutzers mindestens 16 Pixel bewegt wurde. Auf einem Basisbildschirm muss der Finger des Nutzers 16 pixels / 160 dpi bewegen.Das entspricht 2,5 mm, bevor die Geste erkannt wird.

Auf einem Gerät mit einem HD-Display (240 dpi) muss der Finger des Nutzers 16 pixels / 240 dpi bewegen, was 1, 7 mm entspricht. Die Entfernung ist viel kürzer und die App erscheint daher empfindlicher auf den Nutzer.

Um dieses Problem zu beheben, drücken Sie den Gesten-Schwellenwert im Code in dp aus und konvertieren Sie ihn in tatsächliche Pixel. Beispiele:

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...

Das Feld DisplayMetrics.density gibt den Skalierungsfaktor an, der zur Umrechnung von dp-Einheiten in Pixel gemäß der aktuellen Pixeldichte verwendet wird. Auf einem Bildschirm mit mittlerer Dichte entspricht DisplayMetrics.density 1,0 und auf einem Bildschirm mit hoher Dichte 1, 5. Auf einem Bildschirm mit besonders hoher Punktdichte beträgt er 2,0 und auf einem Bildschirm mit niedriger Dichte 0,75. Diese Zahl wird von TypedValue.applyDimension() verwendet, um die tatsächliche Pixelzahl für den aktuellen Bildschirm zu ermitteln.

Vorskalierte Konfigurationswerte verwenden

Mit der Klasse ViewConfiguration können Sie auf gängige Entfernungen, Geschwindigkeiten und Zeiten zugreifen, die vom Android-System verwendet werden. Beispielsweise kann die Entfernung in Pixeln, die vom Framework als Scroll-Schwellenwert verwendet wird, mit getScaledTouchSlop() abgerufen werden:

Kotlin

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

Java

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

Methoden in ViewConfiguration, die mit dem Präfix getScaled beginnen, geben garantiert einen Wert in Pixeln zurück, die unabhängig von der aktuellen Pixeldichte korrekt angezeigt werden.

Vektorgrafiken bevorzugen

Eine Alternative zur Erstellung mehrerer dichtespezifischer Versionen eines Bildes besteht darin, nur eine Vektorgrafik zu erstellen. Bei Vektorgrafiken wird ein Bild mit XML erstellt, um Pfade und Farben zu definieren, anstatt Pixel-Bitmaps zu verwenden. Daher können Vektorgrafiken ohne Skalierungsartefakte auf jede beliebige Größe skaliert werden. Sie sind jedoch normalerweise am besten für Illustrationen wie Symbole und nicht für Fotos geeignet.

Vektorgrafiken werden oft als SVG-Dateien (Scaling Vector Graphics) zur Verfügung gestellt. Dieses Format wird jedoch von Android nicht unterstützt, sodass Sie SVG-Dateien in das Vektor-Drawable-Format von Android konvertieren müssen.

So können Sie ein SVG mit Vector Asset Studio von Android Studio in ein Vektor-Drawable konvertieren:

  1. Klicken Sie im Fenster Project mit der rechten Maustaste auf das Verzeichnis res und wählen Sie New > Vector Asset aus.
  2. Wählen Sie Lokale Datei (SVG, PSD) aus.
  3. Suchen Sie die Datei, die Sie importieren möchten, und nehmen Sie die gewünschten Anpassungen vor.

    Ein Bild, das zeigt, wie SVGs in Android Studio importiert werden
    Abbildung 2: SVG mit Android Studio importieren.

    Möglicherweise siehst du im Fenster Asset Studio einige Fehler, die darauf hinweisen, dass Vektor-Drawables einige Eigenschaften der Datei nicht unterstützen. Der Import der Datei wird dadurch nicht verhindert. Die nicht unterstützten Attribute werden ignoriert.

  4. Klicke auf Weiter.

  5. Bestätigen Sie im nächsten Bildschirm die Quellgruppe, an der die Datei in Ihrem Projekt gespeichert werden soll, und klicken Sie auf Fertigstellen.

    Da ein Vektor-Drawable für alle Pixeldichten verwendet werden kann, wird diese Datei in dein Standard-Drawables-Verzeichnis aufgenommen, wie in der folgenden Hierarchie gezeigt. Sie müssen keine dichtespezifischen Verzeichnisse verwenden.

    res/
      drawable/
        ic_android_launcher.xml
    

Weitere Informationen zum Erstellen von Vektorgrafiken findest du in der Dokumentation zu Vektor-Drawable.

Alternative Bitmaps bereitstellen

Um auf Geräten mit unterschiedlichen Pixeldichten eine gute Grafikqualität zu erzielen, solltest du mehrere Versionen jeder Bitmap in deiner App bereitstellen – eine für jeden Dichte-Bucket mit einer entsprechenden Auflösung. Andernfalls muss Android Ihre Bitmap so skalieren, dass sie auf jedem Bildschirm denselben sichtbaren Bereich einnimmt. Dadurch werden Artefakte wie Unkenntlichmachungen skaliert.

Bild, das die relativen Größen von Bitmaps mit unterschiedlichen Dichten zeigt
Abbildung 3: Relative Größen für Bitmaps in Buckets unterschiedlicher Dichte.

In Ihren Apps sind mehrere Dichte-Buckets verfügbar. In Tabelle 1 werden die verschiedenen verfügbaren Konfigurationsqualifizierer und die Bildschirmtypen beschrieben, für die sie gelten.

Tabelle 1 Konfigurationsqualifizierer für verschiedene Pixeldichten.

Dichtekennzeichner Beschreibung
ldpi Ressourcen für Bildschirme mit niedriger Dichte (ldpi) (~120 dpi)
mdpi Ressourcen für Bildschirme mittlerer Dichte (mdpi) (~160 dpi) Das ist die Referenzdichte.
hdpi Ressourcen für HDPI-Bildschirme (~240 dpi)
xhdpi Ressourcen für Bildschirme mit extrahoher Punktdichte (xhdpi) (~320 dpi)
xxhdpi Ressourcen für Bildschirme mit extrahoher Punktdichte (xxhdpi) (~480 dpi)
xxxhdpi Ressourcen für den Einsatz mit extrem hoher Dichte (xxxhdpi) (ca. 640 dpi).
nodpi Ressourcen für alle Altersgruppen. Dies sind dichteunabhängige Ressourcen. Das System skaliert keine Ressourcen, die mit diesem Qualifier getaggt sind, unabhängig von der aktuellen Bildschirmdichte.
tvdpi Ressourcen für Bildschirme zwischen mdpi und hdpi; ungefähr 213 dpi. Dies wird nicht als „primäre“ Dichtegruppe betrachtet. Sie ist hauptsächlich für Fernsehgeräte vorgesehen und wird von den meisten Apps nicht benötigt. Die Bereitstellung von MDPI- und hdpi-Ressourcen reicht für die meisten Apps aus und das System skaliert sie entsprechend. Wenn Sie tvdpi-Ressourcen bereitstellen müssen, legen Sie ihre Größe auf einen Faktor von 1,33 * mdpi fest. Ein Bild mit 100 × 100 Pixel für mdpi-Bildschirme hat z. B. 133 × 133 Pixel für tvdpi.

Um alternative Bitmap-Drawables für verschiedene Dichten zu erstellen, folge dem Skalierungsverhältnis von 3:4:6:8:12:16 zwischen den sechs primären Dichten. Wenn Sie beispielsweise ein Bitmap-Drawable mit 48 × 48 Pixeln für Bildschirme mittlerer Punktdichte haben, sind die Größen:

  • 36 x 36 (0,75 x) für geringe Dichte (ldpi)
  • 48 x 48 (1,0-fache Baseline) für mittlere Dichte (mdpi)
  • 72 x 72 (1,5-fach) für hohe Dichte (hdpi)
  • 96 x 96 (2,0 x) für besonders hohe Dichte (xhdpi)
  • 144 x 144 (3,0 x) für besonders hohe Dichte (xxhdpi)
  • 192 x 192 (4,0x) für eine besonders hohe Dichte (xxxhdpi)

Platzieren Sie die generierten Bilddateien im entsprechenden Unterverzeichnis unter 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

Jedes Mal, wenn Sie auf @drawable/awesomeimage verweisen, wählt das System die entsprechende Bitmap basierend auf den DPI-Werten des Bildschirms aus. Wenn Sie für diese Dichte keine dichtespezifische Ressource bereitstellen, sucht das System nach der nächstbesten Übereinstimmung und skaliert sie für den Bildschirm.

Tipp:Wenn Sie Drawable-Ressourcen haben, die das System nicht skalieren soll, z. B. wenn Sie zur Laufzeit selbst einige Anpassungen am Image vornehmen, speichern Sie sie in einem Verzeichnis mit dem Konfigurationsqualifizierer nodpi. Ressourcen mit diesem Qualifizierer gelten als dichteunabhängig und werden vom System nicht skaliert.

Weitere Informationen zu anderen Konfigurationsqualifizierern und dazu, wie Android die entsprechenden Ressourcen für die aktuelle Bildschirmkonfiguration auswählt, findest du in der Übersicht über App-Ressourcen.

App-Symbole in Mipmap-Verzeichnissen einfügen

Wie bei anderen Bitmap-Assets müssen Sie dichtespezifische Versionen Ihres App-Symbols bereitstellen. Bei einigen App-Launchern wird das App-Symbol jedoch bis zu 25 % größer angezeigt als vom Dichte-Bucket des Geräts gefordert.

Wenn der Dichte-Bucket eines Geräts beispielsweise xxhdpi ist und das größte von Ihnen angegebene App-Symbol in drawable-xxhdpi vorhanden ist, vergrößert der App Launcher dieses Symbol, wodurch es weniger gestochen scharf erscheint.

Um dies zu vermeiden, sollten Sie alle Ihre App-Symbole in mipmap-Verzeichnissen und nicht in drawable-Verzeichnissen ablegen. Im Gegensatz zu drawable-Verzeichnissen werden alle mipmap-Verzeichnisse im APK beibehalten, auch wenn Sie dichtespezifische APKs erstellen. Dadurch können Launcher-Apps die beste Auflösung auswählen, die auf dem Startbildschirm angezeigt werden sollen.

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

Im vorherigen Beispiel eines xxhdpi-Geräts kannst du ein Launcher-Symbol mit höherer Punktdichte im Verzeichnis mipmap-xxxhdpi angeben.

Richtlinien zur Symbolgestaltung finden Sie unter Systemsymbole.

Weitere Informationen zum Erstellen von App-Symbolen finden Sie unter App-Symbole mit Image Asset Studio erstellen.

Hinweise zu ungewöhnlichen Dichteproblemen

In diesem Abschnitt wird beschrieben, wie Android die Skalierung von Bitmaps bei verschiedenen Pixeldichten durchführt und wie Sie weiter steuern können, wie Bitmaps mit unterschiedlichen Dichten gezeichnet werden. Wenn Ihre App die Grafiken nicht manipuliert oder bei der Ausführung mit unterschiedlichen Pixeldichten Probleme aufgetreten sind, können Sie diesen Abschnitt ignorieren.

Um besser zu verstehen, wie Sie bei der Bearbeitung von Grafiken während der Laufzeit mehrere Dichten unterstützen können, müssen Sie wissen, wie das System den richtigen Maßstab für Bitmaps gewährleistet. Dazu gehen Sie so vor:

  1. Vorskalierung von Ressourcen wie Bitmap-Drawables

    Abhängig von der Dichte des aktuellen Bildschirms verwendet das System alle dichtespezifischen Ressourcen aus Ihrer App. Wenn Ressourcen nicht in der richtigen Dichte verfügbar sind, lädt das System die Standardressourcen und skaliert sie nach Bedarf. Das System geht davon aus, dass Standardressourcen (aus einem Verzeichnis ohne Konfigurationsqualifizierer) für die Referenzpixeldichte (mdpi) entwickelt wurden, und passt die Größe dieser Bitmaps auf die entsprechende Größe für die aktuelle Pixeldichte an.

    Wenn Sie die Dimensionen einer vorab skalierten Ressource anfordern, gibt das System Werte zurück, die die Dimensionen nach der Skalierung darstellen. Beispielsweise wird eine Bitmap, die mit 50 x 50 Pixeln für einen MDPI-Bildschirm erstellt wurde, auf einem HDPI-Bildschirm auf 75 x 75 Pixel skaliert (wenn es keine alternative Ressource für hdpi gibt), und das System meldet die Größe als solche.

    Es gibt einige Situationen, in denen Sie möglicherweise nicht möchten, dass Android eine Ressource vorab skaliert. Der einfachste Weg, eine Vorskalierung zu vermeiden, besteht darin, die Ressource mit dem Konfigurationsqualifizierer nodpi in einem Ressourcenverzeichnis abzulegen. Beispiele:

    res/drawable-nodpi/icon.png

    Wenn das System die Bitmap icon.png aus diesem Ordner verwendet, wird sie nicht anhand der aktuellen Gerätedichte skaliert.

  2. Automatische Skalierung von Pixeldimensionen und -koordinaten

    Sie können die Vorskalierung von Dimensionen und Bildern deaktivieren, indem Sie android:anyDensity im Manifest auf "false" setzen. Alternativ können Sie für Bitmap programmatisch inScaled auf "false" setzen. In diesem Fall skaliert das System automatisch alle absoluten Pixelkoordinaten und Pixeldimensionswerte zum Zeitpunkt der Zeichnung. Dadurch wird sichergestellt, dass pixeldefinierte Bildschirmelemente weiterhin in etwa derselben physischen Größe angezeigt werden, wie sie bei der Basis-Pixeldichte (MDPI) dargestellt werden können. Das System verarbeitet diese Skalierung transparent an die App und meldet die skalierten Pixeldimensionen an die App anstelle der physischen Pixeldimensionen.

    Angenommen, ein Gerät hat einen WVGA-Bildschirm mit hoher Dichte, der 480 x 800 groß ist und etwa dieselbe Größe wie ein herkömmlicher HVGA-Bildschirm hat. Es wird jedoch eine App ausgeführt, die die Vorskalierung deaktiviert hat. In diesem Fall „liegt“ das System der App bei der Abfrage der Bildschirmabmessungen vor und meldet 320 × 533, die ungefähre MDPI-Übersetzung für die Pixeldichte.

    Wenn die Anwendung Zeichenvorgänge durchführt, wie z. B. das Entwerten eines Rechtecks von (10, 10) auf (100,100), wandelt das System die Koordinaten um, indem es sie entsprechend skaliert, und macht die Region (15,15) auf (150, 150). Diese Abweichung kann zu unerwartetem Verhalten führen, wenn Ihre Anwendung die skalierte Bitmap direkt bearbeitet. Dies gilt jedoch als angemessene Kompromisse, um die bestmögliche Anwendungsleistung zu erzielen. In diesem Fall lesen Sie den Artikel dp-Einheiten in Pixeleinheiten konvertieren.

    Normalerweise wird die Vorskalierung nicht deaktiviert. Wenn du mehrere Bildschirme unterstützen möchtest, folge am besten den auf dieser Seite beschriebenen grundlegenden Techniken.

Wenn Ihre App Bitmaps manipuliert oder auf andere Weise direkt mit Pixeln auf dem Bildschirm interagiert, müssen Sie möglicherweise zusätzliche Schritte ausführen, um verschiedene Pixeldichten zu unterstützen. Wenn Sie beispielsweise auf Touch-Gesten reagieren, indem Sie die Anzahl der Pixel zählen, die ein Finger kreuzt, müssen Sie die entsprechenden dichteunabhängigen Pixelwerte anstelle der tatsächlichen Pixel verwenden. Sie können aber zwischen dp- und px-Werten konvertieren.

An allen Pixeldichten testen

Testen Sie Ihre App auf mehreren Geräten mit unterschiedlichen Pixeldichten, damit die UI korrekt skaliert wird. Testen Sie nach Möglichkeit auf einem physischen Gerät. Verwenden Sie den Android-Emulator, wenn Sie keinen Zugriff auf physische Geräte für die verschiedenen Pixeldichten haben.

Wenn Sie an physischen Geräten testen, aber keine Geräte kaufen möchten, können Sie mit Firebase Test Lab auf Geräte in einem Google-Rechenzentrum zugreifen.