Formule px à dp, dp à px android


150

J'essaie de calculer une quantité variable de pixels en pixels indépendants de la densité et vice-versa.

Cette formule (px to dp): dp = (int)(px / (displayMetrics.densityDpi / 160));ne fonctionne pas sur les petits appareils car elle est divisée par zéro.

Voici ma formule dp en px:

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

Quelqu'un pourrait-il me donner des conseils?


1
Conversion d'unités dp en unités de pixels developer.android.com/guide/practices/…
Padma Kumar

@Bram: Je pense que votre formule est très bien. Comment obtiendrez-vous une division par zéro? displayMetrics.densityDpi sera soit 120, 160, 240 ou 320, jamais 0.
ct_rob

Je suis d'accord avec @ct_rob. La valeur minimale de displayMetrics.densityDpi / 160 sera de 0,75. Vous devez avoir casté en int au mauvais endroit.
TomTaila

Réponses:


318

Remarque: la solution largement utilisée ci-dessus est basée sur displayMetrics.density. Cependant, la documentation explique que cette valeur est une valeur arrondie, utilisée avec l'écran «buckets». Par exemple. sur mon Nexus 10, il renvoie 2, où la valeur réelle serait 298 dpi (réel) / 160 dpi (par défaut) = 1,8625.

En fonction de vos besoins, vous pourriez avoir besoin de la transformation exacte, qui peut être réalisée comme ceci:

[Modifier] Ceci n'est pas destiné à être mélangé avec l'unité dp interne d'Android, car cela est bien sûr toujours basé sur les seaux d'écran. Utilisez-le là où vous voulez une unité qui devrait rendre la même taille réelle sur différents appareils.

Convertir dp en pixel:

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

Convertir le pixel en dp:

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

Notez qu'il existe des propriétés xdpi et ydpi, que vous voudrez peut-être distinguer, mais je ne peux pas imaginer un affichage sain où ces valeurs diffèrent considérablement.


8
Cela ne calculera pas la valeur correcte pour dp / px sur de nombreux appareils (y compris votre Nexus 10)! Comme vous le dites, displayMetrics.density est arrondi au bucket d'écran le plus proche, mais l'unité dp l'est aussi! Essayez de dessiner un objet de 160dp de large et juste en dessous, vous dessinez un autre objet de dpToPx (160) pixels de large et vous verrez que la taille des deux objets est différente. De plus, certains téléphones (tels que Galaxy Mini et Galaxy S3 Mini) rapportent des valeurs complètement fausses pour xdpi / ydpi, donc sur ces téléphones, vos méthodes renverront des résultats complètement faux.
nibarius

@nibarius Oui, vous ne pouvez pas mélanger les calculs de dp en boîte d'Android avec ce qui précède. La solution ci-dessus est considérée comme une valeur indépendante de la densité distincte basée sur la physique exacte du dispositif. Nécessaire par exemple lorsque vous souhaitez afficher une ligne de la même longueur réelle exacte sur différents appareils. Bien sûr, si les entrées xdpi / ydpi ne sont pas correctement définies par certains périphériques, cela ne fonctionnera pas là-bas.
Bachi

1
xdpi et ydpi ne doivent pas être utilisés, car ils sont inexacts sur de nombreux appareils, parfois de beaucoup. Seul DisplayMetrics.densityDpi est fiable, ce qui est regrettable, car il est imprécis de par sa conception. Voir le fil de discussion du forum Google pour plus d'informations: groups.google.com/forum/#!topic/android-developers/g56jV0Hora0
Mark McClelland

1
J'ai trouvé cette réponse et utilisé en fait de nombreuses solutions similaires, mais je viens de parcourir la documentation et j'ai trouvé que getDimensionPixelOffSet avec une dimension donnée (déclarée dans dip / dp) renvoie le décalage en pixels, tout comme le code fait à la main. J'ai testé et travaillé parfaitement. J'espère que ça aide!
william gouvea le

1
Mehhh, cela ne donne pas une valeur correcte. Essayez: Resources r = getResources (); float px = TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP, 14, r.getDisplayMetrics ()); Comme décrit ici: stackoverflow.com/questions/4605527/converting-pixels-to-dp
Rik van Velzen

103

J'ai résolu mon problème en utilisant les formules suivantes. Que d'autres personnes en bénéficient.

dp en px:

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

px à dp:

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

27
@Vame L'ajout de 0,5 est utilisé pour arrondir à la valeur entière la plus proche. Le 0,5 est ajouté, puis le résultat du calcul est converti en un entier, ce qui le fait tronquer la mantisse et laisse la caractéristique comme une valeur entière correctement arrondie.
Kelly Copley

3
Ce n'est pas toujours correct. Quand j'ai deux mises en page l'une à l'intérieur de l'autre et que j'arrondis les coins de chaque vue (une en utilisant dp, l'autre conversion en dp), les coins ne correspondent pas!
Marek

Je n'ai eu aucun problème avec cette technique. Je l'ai utilisé pour différentes mises en page et cela a toujours fonctionné comme prévu. Bien sûr, je l'ai utilisé il y a quelques années et je ne suis pas sûr que cela fonctionne toujours. Peut-être pourriez-vous essayer l'autre technique dans la réponse de PanaVTEC. Il se peut aussi que les arrondis ne se limitent pas à des calculs dp / px.
Bram

5
C'est la même solution que celle adoptée sur le site des développeurs Android, donc je suppose que c'est correct :). http://developer.android.com/guide/practices/screens_support.html#dips-pels
alocaly

1
+1 Merci mec. A travaillé comme un charme! Merci de partager cette solution avec nous.
Simon Dorociak

34

px à dp:

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

7
C'est de DP à PX, mais avec cette correction: typedValue.applyDimension( TypedValue.COMPLEX_UNIT_PX, valueInDp , getResources() .getDisplayMetrics());de PX à DP, c'est aussi la meilleure réponse
PaNaVTEC

1
@PaNaVTEC, la solution que vous avez référencée pour dp en px est incorrecte. applyDimensionne produit que des pixels. Voir android.googlesource.com/platform/frameworks/base/+/refs/heads/… pour lequel aucune conversion n'est effectuée COMPLEX_UNIT_PX.
Stephen Niedzielski

Cette réponse est tout simplement fausse. Il renvoie les valeurs finales en pixels, jamais en dp. Si vous regardez le code source, pour le cas TypedValue.COMPLEX_UNIT_DIP, il value * metrics.densityest retourné là où vous en avez réellement besoin value * metrics.density. Alors ce que vous getting` valueInDp` est pas dans dppour valueInPxen px.
bitbybit

^ faute de frappe, je voulais dire "... où vous avez réellement besoin value / metrics.density"
bitbybit

31

Une manière efficace jamais

DP à Pixel:

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

Pixel à DP:

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

J'espère que ceci vous aidera.


4
Mieux car il n'est pas nécessaire d'utiliser Context :) Merci.
Ayaz Alifov

2
Meilleure réponse! Malheureusement, la réponse «Marqué comme correct» est erronée. D'autant qu'il utilise displayMetrics.xdpice qui est différent sur n'importe quel appareil. Malheureusement, cette mauvaise réponse a également le plus de votes positifs.
toom

agréable. devrait être marqué comme meilleure réponse. kotlin:private fun dpToPx(dp: Int) = (dp * Resources.getSystem().displayMetrics.density).toInt()
Raphael C le

28

Appelez simplement getResources().getDimensionPixelSize(R.dimen.your_dimension)pour convertir des dpunités en pixels


3
Selon la documentation, c'est la même chose que getDimension (), sauf que la valeur renvoyée est convertie en pixels entiers pour être utilisée comme taille. Une conversion de taille implique d'arrondir la valeur de base et de s'assurer qu'une valeur de base non nulle est d'au moins un pixel.
CJBS

14

Utilisez cette fonction

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)

bien que, maintenant que j'y pense ... comment y aura-t-il jamais une division par zéro dans la formule originale de Brams? displayMetrics.densityDpi sera soit 120, 160, 240 ou 320, jamais 0.
ct_rob

2
(displayMetrics.densityDpi / 160)- cette partie peut obtenir 0 sur les petits appareils, là, elle calcule par exemple 120/160, les deux valeurs sont int, ce qui donne 0. Ce qui se termine par (int) (px/0).

ah, tu as raison. Il lui suffit donc d'utiliser un long dans sa formule: 160.0
ct_rob

cette réponse est similaire à celle du haut en raison de DisplayMetrics.DENSITY_DEFAULT a une valeur de 160. mais pouvez-vous s'il vous plaît nous dire ce qu'est le DPI ici comment nous le calculons.
John smith

3

Dans la plupart des cas, les fonctions de conversion sont fréquemment appelées. Nous pouvons l'optimiser en ajoutant la mémorisation. Ainsi, il ne calcule pas chaque fois que la fonction est appelée.

Déclarons un HashMap qui stockera les valeurs calculées.

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

Une fonction qui calcule les valeurs des pixels:

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

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

    }

Une fonction de mémorisation qui renvoie la valeur de HashMap et conserve l'enregistrement des valeurs précédentes.

La mémorisation peut être implémentée de différentes manières en Java. Pour 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 prend en charge la fonction Lambda :

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

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

Merci.


bel ajout aux solutions données.
Bram

Je serais surpris si le calcul de conversion n'était pas au moins aussi rapide qu'une recherche de carte, sans parler d'une recherche synchronisée. Avez-vous des résultats de test pour démontrer que cette optimisation est bénéfique? Sur Android où la mémoire de travail est limitée, échanger la mémoire contre l'effort de calcul n'est pas quelque chose que vous devriez faire sans raison.
Lorne Laliberte


2

Les fonctions ci-dessous ont bien fonctionné pour moi sur tous les appareils.

Il est tiré de 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

Vous pouvez utiliser [DisplayMatrics][1]et déterminer la densité de l'écran. Quelque chose comme ça:

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

Si je me souviens bien, il est préférable d'utiliser un revêtement de sol pour les décalages et l'arrondi pour les largeurs.



1

// pour obtenir en termes de décimal / flottant

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

Utilisez ces extensions 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

N'hésitez pas à utiliser cette méthode que j'ai écrite:

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

0

La réponse acceptée ci-dessus n'est pas entièrement exacte. Selon les informations obtenues en inspectant le code source Android:

Resources.getDimension()et getDimensionPixelOffset()/ getDimensionPixelSize()ne diffèrent que par le fait que le premier retourne floatalors que les deux derniers renvoient la même valeur arrondie de intmanière appropriée. Pour tous, la valeur de retour est en pixels bruts.

Les trois fonctions sont implémentées en appelant Resources.getValue()et en convertissant ainsi obtenues TypedValueen appelant TypedValue.complexToDimension(), TypedValue.complexToDimensionPixelOffset()et TypedValue.complexToDimensionPixelSize(), respectivement.

Par conséquent, si vous souhaitez obtenir une valeur "brute" avec l'unité spécifiée dans la source XML, appelez Resources.getValue()et utilisez les méthodes de la TypedValueclasse.


0

avec l'aide d'autres réponses, j'ai écrit cette fonction.

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

0

Voici une autre façon de le faire en utilisant les extensions 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)

et puis il peut être utilisé comme ça de n'importe où

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

variation sur la réponse ct_robs ci-dessus, si vous utilisez des entiers, cela évite non seulement de diviser par 0, mais produit également un résultat utilisable sur de petits appareils:

dans les calculs d'entiers impliquant une division pour une plus grande précision, multipliez d'abord avant de diviser pour réduire les effets de troncature.

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

5 * 120 = 600 / 160 = 3

au lieu de

5 * (120 / 160 = 0) = 0

si vous voulez un résultat arrondi, faites ceci

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

10 * 5 * 120 = 6000 / 160 = 37 + 5 = 42 / 10 = 4
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.