Supporta densità di pixel diverse

I dispositivi Android non solo hanno schermi di dimensioni diverse (smartphone, tablet, TV e così via), ma anche schermi con dimensioni in pixel diverse. Un dispositivo potrebbe avere 160 pixel per pollice, mentre un altro ne occupa 480 nello stesso spazio. Se non prendi in considerazione queste variazioni di densità dei pixel, il sistema potrebbe ridimensionare le immagini, generando di conseguenza immagini sfocate, oppure le immagini potrebbero essere visualizzate con dimensioni errate.

Questa pagina mostra come progettare l'app per supportare diverse densità di pixel utilizzando unità di misura indipendenti dalla risoluzione e fornendo risorse bitmap alternative per ciascuna densità di pixel.

Guarda il video che segue per una panoramica di queste tecniche.

Per ulteriori informazioni sulla progettazione di asset icona, consulta le linee guida per le icone di material design.

Utilizza pixel indipendenti dalla densità

Evita di utilizzare i pixel per definire le distanze o le dimensioni. La definizione di dimensioni con pixel è un problema perché schermi diversi hanno densità di pixel diverse, quindi lo stesso numero di pixel corrisponde a dimensioni fisiche diverse su dispositivi diversi.

Un'immagine che mostra due esempi di display di dispositivi con densità diverse
Figura 1: due schermi delle stesse dimensioni possono avere un numero diverso di pixel.

Per mantenere le dimensioni visibili dell'interfaccia utente su schermi con densità diverse, progetta l'interfaccia utente utilizzando come unità di misura pixel indipendenti dalla densità (dp). Un dp è un'unità di pixel virtuale che equivale approssimativamente a un pixel su uno schermo a media densità (160 dpi, o densità di riferimento). Android traduce questo valore nel numero opportuno di pixel reali per ciascuna compattezza.

Considera i due dispositivi nella Figura 1. Una visualizzazione di 100 pixel appare molto più grande sul dispositivo a sinistra. Una visualizzazione definita con una larghezza di 100 dp appare delle stesse dimensioni su entrambi gli schermi.

Quando definisci le dimensioni del testo, puoi utilizzare i pixel scalabili (sp) come unità. Per impostazione predefinita, l'unità sp ha le stesse dimensioni di dp, ma viene ridimensionata in base alle dimensioni del testo preferite dall'utente. Non utilizzare mai sp per le dimensioni del layout.

Ad esempio, per specificare la spaziatura tra due visualizzazioni, utilizza dp:

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

Quando specifichi la dimensione del testo, utilizza sp:

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

Converti unità dp in unità pixel

In alcuni casi, è necessario esprimere le dimensioni in dp e poi convertirle in pixel. La conversione delle unità dp in pixel dello schermo è la seguente:

px = dp * (dpi / 160)

Nota: non impostare mai questa equazione come hardcoded per calcolare i pixel. Utilizza invece TypedValue.applyDimension(), che converte automaticamente molti tipi di dimensioni (dp, sp e così via) in pixel.

Immagina un'app in cui viene riconosciuto un gesto di scorrimento o scorrimento dopo che l'utente ha spostato il dito di almeno 16 pixel. Su una schermata di base, il dito dell'utente deve spostare 16 pixels / 160 dpi, che equivale a 1/10 di pollice (o 2,5 mm), prima che il gesto venga riconosciuto.

Su un dispositivo con un display ad alta densità (240 dpi), il dito dell'utente deve muoversi 16 pixels / 240 dpi, pari a 1/15 di pollice (o 1,7 mm). La distanza è molto più breve e l'app appare quindi più sensibile all'utente.

Per risolvere il problema, esprimi la soglia del gesto nel codice in dp e poi convertila in pixel effettivi. Ecco alcuni esempi:

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

Il campo DisplayMetrics.density specifica il fattore di scala utilizzato per convertire le unità dp in pixel in base alla densità dei pixel attuale. Su uno schermo a media densità, il valore di DisplayMetrics.density è 1,0 e su 1,5 su uno schermo ad alta densità. Su uno schermo ad altissima densità, il valore è 2,0, mentre su uno a bassa densità il valore è 0,75. Questo valore viene utilizzato da TypedValue.applyDimension() per ottenere il numero effettivo di pixel per la schermata corrente.

Utilizza valori di configurazione pre-scalati

Puoi utilizzare la classe ViewConfiguration per accedere a distanze, velocità e orari comuni utilizzati dal sistema Android. Ad esempio, la distanza in pixel utilizzati dal framework come soglia di scorrimento può essere ottenuta con getScaledTouchSlop():

Kotlin

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

Java

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

Per i metodi in ViewConfiguration che iniziano con il prefisso getScaled viene garantito che restituiscano un valore nei pixel che vengono visualizzati correttamente, indipendentemente dalla densità di pixel attuale.

Preferisco la grafica vettoriale

Un'alternativa alla creazione di più versioni specifiche della densità di un'immagine è la creazione di una sola grafica vettoriale. La grafica vettoriale crea un'immagine utilizzando XML, per definire percorsi e colori, anziché utilizzare bitmap con pixel. Di conseguenza, le immagini vettoriali possono adattarsi a qualsiasi dimensione senza ridimensionare gli artefatti, anche se di solito sono l'ideale per illustrazioni come icone, non per fotografie.

La grafica vettoriale viene spesso fornita come file SVG (Scalable Vector Graphics), ma Android non supporta questo formato, quindi devi convertire i file SVG nel formato disegno vettoriale di Android.

Puoi convertire un file SVG in un disegno vettoriale utilizzando Vector Asset Studio di Android Studio nel seguente modo:

  1. Nella finestra Progetto, fai clic con il pulsante destro del mouse sulla directory res e seleziona Nuovo > Asset vettoriale.
  2. Seleziona File locale (SVG, PSD).
  3. Individua il file da importare e apporta eventuali modifiche.

    Un&#39;immagine che mostra come importare le immagini SVG in Android Studio
    Figura 2: importazione di un file SVG con Android Studio.

    Potresti notare alcuni errori nella finestra Asset Studio, che indicano che i disegnabili vettoriali non supportano alcune proprietà del file. Questa operazione non impedisce l'importazione del file; le proprietà non supportate vengono ignorate.

  4. Tocca Avanti.

  5. Nella schermata successiva, conferma il set di origine in cui vuoi inserire il file nel progetto e fai clic su Fine.

    Poiché è possibile utilizzare un elemento di disegno vettoriale su tutte le densità di pixel, questo file deve essere inserito nella directory disegnabile predefinita, come mostrato nella gerarchia che segue. Non è necessario utilizzare directory specifiche per la densità.

    res/
      drawable/
        ic_android_launcher.xml
    

Per ulteriori informazioni sulla creazione di grafica vettoriale, leggi la documentazione relativa al VectorDrawable.

Fornire bitmap alternative

Per offrire una buona qualità grafica su dispositivi con densità di pixel diverse, fornisci più versioni di ogni bitmap nell'app, una per ogni bucket di densità, a una risoluzione corrispondente. In caso contrario, Android deve ridimensionare la tua bitmap in modo che occupi lo stesso spazio visibile su ogni schermo, il che genera artefatti di scalabilità come la sfocatura.

Un&#39;immagine che mostra le dimensioni relative delle bitmap con densità diverse
Figura 3: dimensioni relative delle bitmap in bucket di densità diversa.

Sono disponibili diversi bucket di densità da utilizzare nelle app. La tabella 1 descrive i diversi qualificatori di configurazione disponibili e i tipi di schermata a cui si applicano.

Tabella 1. Qualificatori di configurazione per diverse densità di pixel.

Qualificatore di densità Descrizione
ldpi Risorse per schermi a bassa densità (ldpi) (~120 dpi).
mdpi Risorse per schermi a media densità (mdpi) (~160 dpi). Questa è la densità di base.
hdpi Risorse per schermi ad alta densità (hdpi) (~240 dpi).
xhdpi Risorse per schermi ad altissima densità (xhdpi) (~320 dpi).
xxhdpi Risorse per schermi ad altissima densità (xxhdpi) (~480 dpi).
xxxhdpi Risorse per utilizzi di altissima densità (xxxhdpi) (~640 dpi).
nodpi Risorse per tutte le densità. Si tratta di risorse indipendenti dalla densità. Il sistema non scala le risorse contrassegnate con questo qualificatore, indipendentemente dalla densità della schermata corrente.
tvdpi Risorse per schermi con risoluzione compresa tra mdpi e hdpi; circa 213 dpi. Questo non è considerato un gruppo di densità "principale". È destinata principalmente ai televisori e non ne ha bisogno per la maggior parte delle app: fornire risorse mdpi e hdpi è sufficiente per la maggior parte delle app e il sistema le scala in base alle esigenze. Se ritieni necessario fornire risorse tvdpi, ridimensionala con un fattore di 1,33 * mdpi. Ad esempio, un'immagine di 100 x 100 pixel per gli schermi Mdpi è di 133 x 133 pixel per tvdpi.

Per creare disegnabili bitmap alternativi per densità diverse, segui il rapporto di scalabilità 3:4:6:8:12:16 tra le sei densità primarie. Ad esempio, se hai un disegno bitmap di 48 x 48 pixel per schermi a media densità, le dimensioni sono:

  • 36 x 36 (0,75 x) per bassa densità (ldpi)
  • 48 x 48 (1,0x base) per media densità (mdpi)
  • 72 x 72 (1,5 x) per alta densità (hdpi)
  • 96 x 96 (2,0 x) per altissima densità (xhdpi)
  • 144 x 144 (3,0 x) per densità extra-altissima (xxhdpi)
  • 192 x 192 (4,0 x) per densità extra-extra-altissima (xxxhdpi)

Inserisci i file immagine generati nella sottodirectory appropriata in 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

Ogni volta che fai riferimento a @drawable/awesomeimage, il sistema seleziona la bitmap appropriata in base ai DPI dello schermo. Se non fornisci una risorsa specifica per la densità per questa densità, il sistema individua la corrispondenza migliore successiva e la scala per adattarla allo schermo.

Suggerimento: se non vuoi che il sistema scali le risorse di cui disponi, ad esempio quando devi apportare modifiche all'immagine in fase di runtime, inseriscile in una directory con il qualificatore di configurazione nodpi. Le risorse con questo qualificatore sono considerate indipendenti dalla densità e il sistema non le scala.

Per ulteriori informazioni su altri qualificatori di configurazione e su come Android seleziona le risorse appropriate per la configurazione attuale della schermata, consulta la panoramica delle risorse per le app.

Inserisci le icone delle app nelle directory mipmap

Come per altri asset bitmap, devi fornire versioni dell'icona dell'app specifiche per la densità. Tuttavia, alcune app in Avvio app mostrano l'icona dell'app fino al 25% più grande di quella richiesta dal bucket di densità del dispositivo.

Ad esempio, se il bucket di densità di un dispositivo è xxhdpi e l'icona dell'app più grande che fornisci è in drawable-xxhdpi, Avvio app scala questa icona, rendendola meno nitida.

Per evitare che questo accada, inserisci tutte le icone delle app in directory mipmap anziché in directory drawable. A differenza delle directory drawable, tutte le directory mipmap vengono conservate nell'APK, anche se crei APK specifici per la densità. In questo modo le app di avvio possono scegliere l'icona di risoluzione migliore da mostrare nella schermata Home.

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

Nell'esempio precedente di un dispositivo xxhdpi, puoi fornire un'icona in Avvio applicazioni a densità più elevata nella directory mipmap-xxxhdpi.

Per le linee guida sulla progettazione delle icone, consulta la sezione Icone di sistema.

Per informazioni sulla creazione delle icone delle app, consulta Creare icone delle app con Image Asset Studio.

Consigli per problemi di densità non comuni

Questa sezione descrive il modo in cui Android scala le bitmap con densità di pixel diverse e come puoi controllare ulteriormente il modo in cui le bitmap vengono disegnate su densità diverse. A meno che la tua app non manipola la grafica o che tu non abbia riscontrato problemi durante l'esecuzione con densità di pixel diverse, puoi ignorare questa sezione.

Per capire meglio come puoi supportare più densità quando modifichi la grafica in fase di runtime, devi sapere in che modo il sistema contribuisce a garantire la scalabilità corretta per le bitmap. Questa operazione viene eseguita nei seguenti modi:

  1. Pre-scalabilità delle risorse, ad esempio disegnabili bitmap

    In base alla densità della schermata corrente, il sistema utilizza qualsiasi risorsa specifica della densità della tua app. Se le risorse non sono disponibili con la densità corretta, il sistema carica le risorse predefinite e ne esegue lo scale up o lo scale down in base alle esigenze. Il sistema presuppone che le risorse predefinite (quelle di una directory senza qualificatori di configurazione) siano progettate per la densità di pixel di riferimento (mdpi) e ridimensiona le bitmap alle dimensioni appropriate per la densità di pixel corrente.

    Se richiedi le dimensioni di una risorsa prescalata, il sistema restituisce valori che rappresentano le dimensioni dopo la scalabilità. Ad esempio, una bitmap progettata con dimensioni di 50 x 50 pixel per uno schermo mdpi viene scalata a 75 x 75 pixel su uno schermo hdpi (se non esiste una risorsa alternativa per l'hdpi) e il sistema segnala le dimensioni come tali.

    Esistono alcune situazioni in cui potresti non volere che Android prescala una risorsa. Il modo più semplice per evitare la pre-scalabilità è inserire la risorsa in una directory delle risorse con il qualificatore di configurazione nodpi. Ecco alcuni esempi:

    res/drawable-nodpi/icon.png

    Quando il sistema utilizza la bitmap icon.png di questa cartella, non la scala in base alla densità del dispositivo attuale.

  2. Scalabilità automatica di dimensioni e coordinate in pixel

    Puoi disattivare le dimensioni e le immagini di pre-scalabilità impostando android:anyDensity su "false" nel file manifest o in modo programmatico per un Bitmap impostando inScaled su "false". In questo caso, il sistema scala automaticamente tutte le coordinate di pixel assolute e i valori delle dimensioni dei pixel al momento del disegno. Ciò garantisce che gli elementi dello schermo definiti dai pixel vengano comunque visualizzati all'incirca nelle stesse dimensioni fisiche con cui possono essere visualizzati con la densità dei pixel di riferimento (mdpi). Il sistema gestisce questo ridimensionamento in modo trasparente all'app e segnala le dimensioni in pixel scalate all'app, anziché le dimensioni in pixel fisiche.

    Ad esempio, supponi che un dispositivo abbia uno schermo WVGA ad alta densità di 480 x 800 che abbia all'incirca le stesse dimensioni di uno schermo HVGA tradizionale, ma che esegue un'app che ha disabilitato il pre-scala. In questo caso, il sistema "si trova" nell'app quando esegue una query per le dimensioni dello schermo e riporta 320 x 533, la traduzione approssimativa del valore mdpi per la densità dei pixel.

    Poi, quando l'app esegue operazioni di disegno, ad esempio annullando un rettangolo da (10,10) a (100, 100), il sistema trasforma le coordinate ridimensionandole in scala del numero appropriato e invalida la regione da (15,15) a (150, 150). Questa discrepanza potrebbe causare un comportamento imprevisto se la tua app manipola direttamente la bitmap scalata, ma questo è considerato un compromesso ragionevole per garantire le migliori prestazioni possibili dell'app. Se riscontri questa situazione, consulta Convertire unità dp in unità pixel.

    In genere, non viene disattivata la scalabilità preliminare. Il modo migliore per supportare più schermi è seguire le tecniche di base descritte in questa pagina.

Se la tua app manipola i bitmap o interagisce direttamente con i pixel sullo schermo in altro modo, potresti dover svolgere passaggi aggiuntivi per supportare densità di pixel diverse. Ad esempio, se rispondi ai gesti tattili contando il numero di pixel incrociati con un dito, dovrai utilizzare i valori appropriati dei pixel indipendenti dalla densità, invece dei pixel effettivi, ma puoi convertire tra valori dp e px.

Testa su tutte le densità di pixel

Testa l'app su più dispositivi con densità di pixel diverse per assicurarti che l'interfaccia utente si adatti correttamente. Se possibile, esegui i test su un dispositivo fisico; utilizza Android Emulator se non hai accesso a dispositivi fisici per tutte le diverse densità di pixel.

Se vuoi eseguire il test su dispositivi fisici ma non vuoi acquistarli, puoi utilizzare Firebase Test Lab per accedere ai dispositivi in un data center di Google.