Formula px in dp, dp in px android


150

Sto cercando di calcolare una quantità variabile di pixel in pixel indipendenti dalla densità e viceversa.

Questa formula (px to dp): dp = (int)(px / (displayMetrics.densityDpi / 160));non funziona su piccoli dispositivi perché è divisa per zero.

Questa è la mia formula da dp a px:

px = (int)(dp * (displayMetrics.densityDpi / 160));

Qualcuno potrebbe darmi dei suggerimenti?


1
Conversione di unità dp in unità pixel developer.android.com/guide/practices/…
Padma Kumar

@Bram: penso che la tua formula vada bene. Come otterrai una divisione per zero? displayMetrics.densityDpi sarà 120, 160, 240 o 320, mai 0.
ct_rob

Sono d'accordo con @ct_rob. displayMetrics.densityDpi / 160 il valore minimo sarà 0.75. Devi aver lanciato a int nel posto sbagliato.
TomTaila,

Risposte:


318

Nota: la soluzione ampiamente utilizzata sopra è basata su displayMetrics.density. Tuttavia, i documenti spiegano che questo valore è un valore arrotondato, utilizzato con i 'secchi' dello schermo. Per esempio. sul mio Nexus 10 restituisce 2, dove il valore reale sarebbe 298 dpi (reale) / 160 dpi (impostazione predefinita) = 1,8625.

A seconda delle tue esigenze, potresti aver bisogno della trasformazione esatta, che può essere ottenuta in questo modo:

[Modifica] Questo non è pensato per essere mescolato con l'unità dp interna di Android, poiché ovviamente si basa ancora sui secchi dello schermo. Usa questo dove vuoi un'unità che dovrebbe rendere la stessa dimensione reale su dispositivi diversi.

Converti dp in pixel:

public int dpToPx(int dp) {
    DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
    return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));     
}

Converti pixel in dp:

public int pxToDp(int px) {
    DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
    return Math.round(px / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}

Nota che ci sono proprietà xdpi e ydpi, potresti voler distinguere, ma non riesco a immaginare una visualizzazione sana in cui questi valori differiscono notevolmente.


8
Questo non calcolerà il valore corretto per dp / px su molti dispositivi (incluso il tuo Nexus 10)! Come dici tu displayMetrics.density è arrotondato al bucket dello schermo più vicino, ma lo è anche l'unità dp! Prova a disegnare un oggetto largo 160dp e appena sotto di esso disegna un altro oggetto largo dpToPx (160) pixel e vedrai che le dimensioni dei due oggetti sono diverse. Inoltre, alcuni telefoni (come Galaxy Mini e Galaxy S3 Mini) riportano valori completamente errati per xdpi / ydpi, quindi su questi telefoni i tuoi metodi restituiranno risultati completamente sbagliati.
Nibario

@nibarius Sì, non puoi mescolare i calcoli di dp in scatola di Android con quanto sopra. La soluzione sopra è intesa come un valore indipendente dalla densità separato basato sulla fisica esatta del dispositivo. Necessario, ad esempio, dove si desidera mostrare una linea della stessa identica lunghezza reale su dispositivi diversi. Naturalmente se gli ingressi xdpi / ydpi non sono impostati correttamente da alcuni dispositivi, non funzionerà lì.
Bachi,

1
xdpi e ydpi non dovrebbero essere usati, perché sono inaccurati su molti dispositivi, a volte di molto. Solo DisplayMetrics.densityDpi è affidabile, il che è un peccato, dal momento che è impreciso dal design. Vedi il thread del forum di Google per maggiori informazioni: groups.google.com/forum/#!topic/android-developers/g56jV0Hora0
Mark McClelland,

1
Ho trovato questa risposta e in realtà ho usato molte soluzioni simili, ma ho appena esaminato i documenti e ho trovato getDimensionPixelOffSet con una dimensione (dichiarata in dip / dp) che restituisce l'offset in pixel proprio come il codice fatto a mano. Ho testato e lavorato alla perfezione. Spero che sia d'aiuto!
William Gouvea,

1
Mehhh, questo non dà un valore corretto. Prova: risorse r = getResources (); float px = TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP, 14, r.getDisplayMetrics ()); Come descritto qui: stackoverflow.com/questions/4605527/converting-pixels-to-dp
Rik van Velzen

103

Ho risolto il mio problema usando le seguenti formule. Possano altre persone trarne beneficio.

dp in px:

displayMetrics = context.getResources().getDisplayMetrics();
return (int)((dp * displayMetrics.density) + 0.5);

px a dp:

displayMetrics = context.getResources().getDisplayMetrics();
return (int) ((px/displayMetrics.density)+0.5);

27
@Vame L'aggiunta di 0,5 viene utilizzata per arrotondare UP al valore intero più vicino. Viene aggiunto lo 0,5 e quindi il risultato del calcolo viene lanciato come int facendolo troncare la mantissa e lasciando la caratteristica come valore intero correttamente arrotondato.
Kelly Copley,

3
Questo non è sempre corretto. Quando ho due layout uno dentro l'altro e poi giro gli angoli di ogni vista (uno usando dp, altri convertendoli in dp) gli angoli non corrispondono!
Marek,

Non ho avuto problemi con questa tecnica. L'ho usato per diversi layout e ha sempre funzionato come previsto. Ovviamente l'ho usato qualche anno fa e non sono sicuro che funzioni ancora. Forse potresti provare l'altra tecnica nella risposta di PanaVTEC. Potrebbe anche esserci qualcosa di più nell'arrotondare gli angoli oltre ai calcoli dp / px.
Bram,

5
È la stessa soluzione che viene adottata sul sito degli sviluppatori Android, quindi immagino sia corretta :). http://developer.android.com/guide/practices/screens_support.html#dips-pels
alocaly

1
+1 Grazie amico. Ha funzionato come un fascino! Grazie per aver condiviso questa soluzione con noi.
Simon Dorociak,

34

px a dp:

int valueInpx = ...;
int valueInDp= (int) TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, valueInpx , getResources()
                .getDisplayMetrics());

7
Questo è da DP a PX, ma con questa correzione: typedValue.applyDimension( TypedValue.COMPLEX_UNIT_PX, valueInDp , getResources() .getDisplayMetrics());è da PX a DP, anche questa è la risposta migliore
PaNaVTEC

1
@PaNaVTEC, la soluzione a cui hai fatto riferimento per dp a px non è corretta. applyDimensionproduce solo pixel. Vedi android.googlesource.com/platform/frameworks/base/+/refs/heads/… in cui non viene eseguita alcuna conversione COMPLEX_UNIT_PX.
Stephen Niedzielski,

Questa risposta è chiaramente sbagliata. Restituisce i valori finali in pixel, mai in dp. Se guardi il codice sorgente, per il caso TypedValue.COMPLEX_UNIT_DIP, value * metrics.densityviene restituito, dove effettivamente hai bisogno value * metrics.density. Quindi, ciò che si sta getting` valueInDp` NON è in dpper valueInPxin px.
bitbybit

^ errore di battitura, intendevo "... dove effettivamente hai bisogno value / metrics.density"
bitbybit

31

Modo efficiente mai

DP a Pixel:

private int dpToPx(int dp)
{
    return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
}

Pixel a DP:

private int pxToDp(int px)
{
    return (int) (px / Resources.getSystem().getDisplayMetrics().density);
}

Spero che questo ti possa aiutare.


4
Meglio perché non è necessario utilizzare il contesto :) Grazie.
Ayaz Alifov,

2
Migliore risposta! Purtroppo la risposta "Contrassegnato come corretto" è errata. Soprattutto dal momento che utilizza displayMetrics.xdpiche è diverso su qualsiasi dispositivo. Purtroppo, questa risposta sbagliata ha anche il maggior numero di voti.
toom

simpatico. dovrebbe essere contrassegnato come migliore risposta. kotlin:private fun dpToPx(dp: Int) = (dp * Resources.getSystem().displayMetrics.density).toInt()
Raphael C

28

Chiama getResources().getDimensionPixelSize(R.dimen.your_dimension)per convertire da dpunità in pixel


3
Per la documentazione, questo è uguale a getDimension (), tranne per il fatto che il valore restituito viene convertito in pixel interi per l'uso come dimensione. Una conversione delle dimensioni comporta l'arrotondamento del valore di base e la garanzia che un valore di base diverso da zero abbia una dimensione di almeno un pixel.
CJBS,

14

Usa questa funzione

private int dp2px(int dp) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}

6
px = dp * (dpi / 160)

dp = px * (160 / dpi)

sebbene, ora che ci penso ... come ci sarà mai una divisione per zero nella formula originale di Brams? displayMetrics.densityDpi sarà 120, 160, 240 o 320, mai 0.
ct_rob

2
(displayMetrics.densityDpi / 160)- questa parte può ottenere 0 su piccoli dispositivi, lì calcola per esempio 120/160, entrambi i valori sono int, che risulta in 0. Che finisce come (int) (px/0).

ah, hai ragione. Quindi tutto ciò che deve fare è usare un lungo nella sua formula: 160.0
ct_rob

questa risposta è simile a quella superiore a causa di DisplayMetrics.DENSITY_DEFAULT ha un valore di 160. ma puoi per favore facci sapere cos'è DPI qui come lo calcoliamo.
John Smith,

3

Nella maggior parte dei casi, le funzioni di conversione vengono chiamate frequentemente. Possiamo ottimizzarlo aggiungendo memoization. Pertanto, non viene calcolato ogni volta che viene chiamata la funzione.

Dichiariamo una HashMap che memorizzerà i valori calcolati.

private static Map<Float, Float> pxCache = new HashMap<>();

Una funzione che calcola i valori dei pixel:

public static float calculateDpToPixel(float dp, Context context) {

        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        float px = dp * (metrics.densityDpi / 160f);
        return px;

    }

Una funzione di memoization che restituisce il valore da HashMap e mantiene il record dei valori precedenti.

La memoization può essere implementata in diversi modi in Java. Per Java 7 :

public static float convertDpToPixel(float dp, final Context context) {

        Float f = pxCache.get(dp);
        if (f == null) {
            synchronized (pxCache) {
                f = calculateDpToPixel(dp, context);
                pxCache.put(dp, f);
            }

        }

        return f;
    }

Java 8 supporta la funzione Lambda :

public static float convertDpToPixel(float dp, final Context context) {

        pxCache.computeIfAbsent(dp, y ->calculateDpToPixel(dp,context));
}

Grazie.


bella aggiunta alle soluzioni fornite.
Bram,

Sarei sorpreso se il calcolo della conversione non fosse almeno veloce come una ricerca sulla mappa, per non parlare di uno sincronizzato. Hai dei risultati dei test per dimostrare che questa ottimizzazione è vantaggiosa? Su Android in cui la memoria di lavoro è limitata, scambiare la memoria per lo sforzo di calcolo non è qualcosa che dovresti fare senza una buona ragione.
Lorne Laliberte,


2

Di seguito le funzioni hanno funzionato bene per me su tutti i dispositivi.

È tratto da https://gist.github.com/laaptu/7867851

public static float convertPixelsToDp(float px){
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float dp = px / (metrics.densityDpi / 160f);
    return Math.round(dp);
}

public static float convertDpToPixel(float dp){
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float px = dp * (metrics.densityDpi / 160f);
    return Math.round(px);
}

1

È possibile utilizzare [DisplayMatrics][1]e determinare la densità dello schermo. Qualcosa come questo:

int pixelsValue = 5; // margin in pixels
float d = context.getResources().getDisplayMetrics().density;
int margin = (int)(pixelsValue * d);

Come ricordo, è meglio usare pavimenti per offset e arrotondamenti per larghezze.



1

// per ottenere in termini di decimale / virgola mobile

public static float convertPixelsToDp(float px, Context context) {

    Resources resources = context.getResources();
    DisplayMetrics metrics = resources.getDisplayMetrics();
    float dp = px / (metrics.densityDpi / 160f);
    return Math.round(dp);
}



public static float convertDpToPixel(float dp, Context context) {
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float px = dp * (metrics.densityDpi / 160f);
    return Math.round(px);
}


// for  getting in terms of Integer

private int convertPxToDp(int px, Context context) {
    Resources resources = context.getResources();
    return Math.round(px / (resources.getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
}


private int convertDpToPx(int dp, Context context) {
    Resources resources = context.getResources();

    return Math.round(dp * (resources.getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));

}

________________________________________________________________________________

public static float convertPixelsToDp(float px){
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float dp = px / (metrics.densityDpi / 160f);
    return Math.round(dp);
}

public static float convertDpToPixel(float dp){
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float px = dp * (metrics.densityDpi / 160f);
    return Math.round(px);
}


private int convertDpToPx(int dp){
    return Math.round(dp*(getResources().getDisplayMetrics().xdpi/DisplayMetrics.DENSITY_DEFAULT));

}

private int convertPxToDp(int px){
    return Math.round(px/(Resources.getSystem().getDisplayMetrics().xdpi/DisplayMetrics.DENSITY_DEFAULT));
}

1

Usa queste estensioni di Kotlin:

/**
 * Converts Pixel to DP.
 */
val Int.pxToDp: Int
    get() = (this / Resources.getSystem().displayMetrics.density).toInt()

/**
 * Converts DP to Pixel.
 */
val Int.dpToPx: Int
    get() = (this * Resources.getSystem().displayMetrics.density).toInt()

0

Sentiti libero di usare questo metodo che ho scritto:

int dpToPx(int dp)
{
    return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
}

0

La risposta accettata sopra non è completamente precisa. Secondo le informazioni ottenute ispezionando il codice sorgente Android:

Resources.getDimension()e getDimensionPixelOffset()/ getDimensionPixelSize()differiscono solo per il fatto che i primi restituiscono floatmentre gli ultimi due restituiscono lo stesso valore arrotondato in modo intappropriato. Per tutti loro, il valore restituito è in pixel non elaborati.

Tutte le tre funzioni sono implementedy chiamando Resources.getValue()e convertendo così ottenuto TypedValuechiamando TypedValue.complexToDimension(), TypedValue.complexToDimensionPixelOffset()e TypedValue.complexToDimensionPixelSize(), rispettivamente.

Pertanto, se si desidera ottenere un valore "grezzo" insieme all'unità specificata nell'origine XML, chiamare Resources.getValue()e utilizzare i metodi della TypedValueclasse.


0

con l'aiuto di altre risposte ho scritto questa funzione.

public static int convertToPixels(Context context, int nDP)
{
        final float conversionScale = context.getResources().getDisplayMetrics().density; 
        return (int) ((nDP * conversionScale) + 0.5f) ;
}

0

Ecco un altro modo per farlo usando le estensioni di kotlin:

val Int.dpToPx: Int
    get() = Math.round(this * Resources.getSystem().displayMetrics.density)

val Int.pxToDp: Int
    get() = Math.round(this / Resources.getSystem().displayMetrics.density)

e quindi può essere utilizzato in questo modo da qualsiasi luogo

12.dpToPx

244.pxToDp

0
DisplayMetrics displayMetrics = contaxt.getResources()
            .getDisplayMetrics();

    int densityDpi = (int) (displayMetrics.density * 160f);
    int ratio = (densityDpi / DisplayMetrics.DENSITY_DEFAULT);
    int px;
    if (ratio == 0) {
        px = dp;
    } else {
        px = Math.round(dp * ratio);

    }

0

variazione sulla risposta ct_robs sopra, se si utilizzano numeri interi, che non solo evita la divisione per 0 produce anche un risultato utilizzabile su piccoli dispositivi:

nei calcoli interi che coinvolgono la divisione per la massima precisione, moltiplicarli prima di dividere per ridurre gli effetti di troncamento.

px = dp * dpi / 160
dp = px * 160 / dpi

5 * 120 = 600 / 160 = 3

invece di

5 * (120 / 160 = 0) = 0

se vuoi un risultato arrotondato, fallo

px = (10 * dp * dpi / 160 + 5) / 10
dp = (10 * px * 160 / dpi + 5) / 10

10 * 5 * 120 = 6000 / 160 = 37 + 5 = 42 / 10 = 4
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.