Formula px to dp, dp to px android
Asked Answered
R

21

162

I am trying to calculate a variable amount of pixels to density independent pixels and vice-versa.

This formula (px to dp): dp = (int)(px / (displayMetrics.densityDpi / 160)); does not work on small devices because it is divided by zero.

This is my dp to px formula:

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

Could someone give me some pointers?

Reinhart answered 29/11, 2011 at 10:38 Comment(3)
Converting dp units to pixel units developer.android.com/guide/practices/…Eeg
@Bram: I think your formula is just fine. How will you get a division by zero? displayMetrics.densityDpi will be either 120, 160, 240 or 320, never 0.Stephaniestephannie
I agree with @Stephaniestephannie . displayMetrics.densityDpi / 160 minimum value will be 0.75. You must have been casting to int in the incorrect place.Wauters
G
331

Note: The widely used solution above is based on displayMetrics.density. However, the docs explain that this value is a rounded value, used with the screen 'buckets'. Eg. on my Nexus 10 it returns 2, where the real value would be 298dpi (real) / 160dpi (default) = 1.8625.

Depending on your requirements, you might need the exact transformation, which can be achieved like this:

[Edit] This is not meant to be mixed with Android's internal dp unit, as this is of course still based on the screen buckets. Use this where you want a unit that should render the same real size on different devices.

Convert dp to pixel:

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

Convert pixel to dp:

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

Note that there are xdpi and ydpi properties, you might want to distinguish, but I can't imagine a sane display where these values differ greatly.

Grantland answered 1/7, 2013 at 17:17 Comment(9)
This will not calculate the correct value for dp/px on many devices (including your Nexus 10)! As you say displayMetrics.density is rounded to the nearest screen bucket, but so is the dp unit! Try drawing one object that is 160dp wide and just below it you draw another object that is dpToPx(160) pixels wide and you will see that the size of the two objects are different. Also some phones (such as Galaxy Mini and Galaxy S3 Mini) report completely wrong values for xdpi/ydpi so on these phones your methods will return completely wrong results.Charming
@Charming Yes you cannot mix Android's boxed dp calculations with the above. The above solution is meant as a separate density independent value based on the exact device physics. Needed eg where you want to show a line the exact same real length on different devices. Of course if the xdpi/ydpi inputs are not correctly set by some devices, it wont't work there.Grantland
Thanks for the clarification, when you need a fixed physical size this is the way to go.Charming
xdpi and ydpi should not be used, because they are inaccurate on many devices, sometimes by a lot. Only DisplayMetrics.densityDpi is reliable, which is unfortunate, since it is imprecise by design. See Google forum thread for more info: groups.google.com/forum/#!topic/android-developers/g56jV0Hora0Kimberlite
I found this response and actually used many similar solutions but I just went through the docs and found getDimensionPixelOffSet with given a dimension(declared in dip/dp) returns the offset in pixels just like the handmade code. I tested and worked flawlessly. Hope it helps!Atlee
Mehhh, this doesn't give correct value. Try: Resources r = getResources(); float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, r.getDisplayMetrics()); As described here: https://mcmap.net/q/36300/-converting-pixels-to-dpChore
why are you hardcoding displayMetrics.xdpi ? this answer doesn't use xdpi: https://mcmap.net/q/36299/-formula-px-to-dp-dp-to-px-androidBuhl
this should be easier (int) ((dp * App.getContext().getResources().getDisplayMetrics().density) + 0.5)Buhl
Apparently return dp * context.getResources().getDisplayMetrics().density; worksTamar
R
103

I solved my problem by using the following formulas. May other people benefit from it.

dp to px:

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

px to dp:

displayMetrics = context.getResources().getDisplayMetrics();
return (int) ((px/displayMetrics.density)+0.5);
Reinhart answered 13/12, 2011 at 13:55 Comment(7)
@Vame The adding of 0.5 is used to round UP to the nearest integer value.. The 0.5 is added and then the result of the calculation is cast as an int causing it to truncate the mantissa and leaving characteristic as a properly rounded integer value.Dodecagon
This is not always correct. When I have two layouts one inside another and then I round corners of each view (one by using dp, other converting to dp) corners doesn't match!Poise
I didn't have any problems with this technique. I used this for different layouts and it always worked as expected. Of course I used this few years ago and I am not sure if it still works. Perhaps you could try the other technique in PanaVTEC's answer. It could also be that there is more to rounding corners than just dp / px calculations.Reinhart
the density value is not exact, but rather adjusted in steps. From the docs: This value does not exactly follow the real screen size (as given by xdpi and ydpi, but rather is used to scale the size of the overall UI in steps based on gross changes in the display dpi. For example, a 240x320 screen will have a density of 1 even if its width is 1.8", 1.3", etc.. If you need an exact conversion, better use displayMetrics.xdpi and displayMetrics.ydpi (see my example below)Grantland
It's the same solution that is adopted on the Android developer site, so I guess it's correct :) . http://developer.android.com/guide/practices/screens_support.html#dips-pelsSessoms
+1 Thanks man. Worked like a charm! Thank you for sharing this solution with us.Now
Just to clarify that this method will not always give the correct value. Use the accepted answer instead.Ryurik
C
44

Efficient way ever

DP to Pixel:

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

Pixel to DP:

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

Hope this will help you.

Chapa answered 13/1, 2016 at 10:4 Comment(4)
Better because there is no need to use Context :) Thanks.Lurlene
Best answer! Unfortunately the "Marked as correct" answer is wrong. Especially since it uses displayMetrics.xdpi which is different on any device. Sadly, this wrong answer additionally has the most upvotes.Distinguish
nice. should be marked as best answer. kotlin: private fun dpToPx(dp: Int) = (dp * Resources.getSystem().displayMetrics.density).toInt()Centric
Not better, Resources.getSystem() is not always correctTamar
M
36

px to dp:

int valueInpx = ...;
int valueInDp= (int) TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, valueInpx , getResources()
                .getDisplayMetrics());
Mina answered 29/11, 2011 at 11:8 Comment(4)
This is from DP to PX, but with this correction: typedValue.applyDimension( TypedValue.COMPLEX_UNIT_PX, valueInDp , getResources() .getDisplayMetrics()); is from PX to DP, also this is the best answerWeaver
@PaNaVTEC, the solution you referenced for dp to px is incorrect. applyDimension only yields pixels. See android.googlesource.com/platform/frameworks/base/+/refs/heads/… wherein no conversion is performed for COMPLEX_UNIT_PX.Ephemera
This answer is plain wrong. It returns final values in pixels, never in dp. If you look at the source code, for the case TypedValue.COMPLEX_UNIT_DIP, value * metrics.density is returned, where you actually need value * metrics.density. So what you are getting` valueInDp` is NOT in dp for valueInPx in px.Pooh
^typo, I meant "...where you actually need value / metrics.density"Pooh
E
29

Just call getResources().getDimensionPixelSize(R.dimen.your_dimension) to convert from dp units to pixels

Ekaterinoslav answered 20/2, 2014 at 15:24 Comment(1)
Per the documentation, this is the same as getDimension(), except the returned value is converted to integer pixels for use as a size. A size conversion involves rounding the base value, and ensuring that a non-zero base value is at least one pixel in size.Erythrism
W
14

Use This function

private int dp2px(int dp) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
Wiliness answered 19/6, 2015 at 9:24 Comment(0)
T
7

Use these Kotlin extensions:

/**
 * 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()
Tuesday answered 25/12, 2019 at 14:17 Comment(0)
S
5
px = dp * (dpi / 160)

dp = px * (160 / dpi)
Stephaniestephannie answered 29/11, 2011 at 10:54 Comment(4)
although, now that i think about it...how will there ever be a divsion by zero in Brams original formula? displayMetrics.densityDpi will be either 120, 160, 240 or 320, never 0.Stephaniestephannie
(displayMetrics.densityDpi / 160) - this part can get 0 on small devices, there it calculates for example 120/160, both values are int, which results in 0. Which ends up as (int) (px/0).Proustite
ah, you're right. So all he needs to do is use a long in his formula: 160.0Stephaniestephannie
this answer is similar to upper one because of DisplayMetrics.DENSITY_DEFAULT has 160 value. but can you please let us know what is DPI here how we calculate it.Gredel
H
3

In most of the cases, conversion functions are called frequently. We can optimize it by adding memoization. So,it does not calculate every-time the function is called.

Let's declare a HashMap which will store the calculated values.

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

A function which calculates pixel values :

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

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

    }

A memoization function which returns the value from HashMap and maintains the record of previous values.

Memoization can be implemented in different ways in Java. For 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 supports Lambda function :

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

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

Thanks.

Hifalutin answered 26/2, 2015 at 6:52 Comment(2)
nice addition to the given solutions.Reinhart
I would be surprised if the conversion calculation wasn't at least as fast as a map lookup, not to mention a synchronized one. Do you have any test results to demonstrate this optimization is beneficial? On Android where working memory is limited, trading memory for calculation effort isn't something you should do without good reason.Illyricum
R
2

Elegant kotlin solution :)

val Int.dp get() = this / (Resources.getSystem().displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
val Float.dp get() = this / (Resources.getSystem().displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)

val Int.px get() = this * Resources.getSystem().displayMetrics.density
val Float.px get() = this * Resources.getSystem().displayMetrics.density

Usage:

val dpValue = 2.dp
val pxFromDpValue = 2.px

Improtant:

I am not sure if Resources.getSystem() will work correctly with orientation changes.

If want to work in for example fragment or activity just add it it in to base fragment or base activity and use it like this:

abstract class BaseFragment : Fragment() {

    val Int.dp get() = this / (resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
    val Float.dp get() = this / (resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)

    val Int.px get() = this * resources.displayMetrics.density
    val Float.px get() = this * resources.displayMetrics.density

    .......
}
Reasoned answered 11/11, 2021 at 11:59 Comment(0)
S
1

You can use [DisplayMatrics][1] and determine the screen density. Something like this:

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

As I remember it's better to use flooring for offsets and rounding for widths.

Sublingual answered 29/11, 2011 at 11:0 Comment(0)
K
1

try this http://labs.skinkers.com/content/android_dp_px_calculator/

Kassala answered 29/11, 2011 at 12:46 Comment(2)
I prefer this one as it includes xxhdpi and xxxhdpi and other units of measure.Tussore
Link in answer is dead - "The domain Skinkers.com may be for sale. Click here to inquire about this domain."Skyline
T
1

If you're looking for an online calculator for converting DP, SP, inches, millimeters, points or pixels to and from one another at different screen densities, this is the most complete tool I know of.

Tussore answered 11/6, 2014 at 10:8 Comment(0)
M
1

Below funtions worked well for me across devices.

It is taken from 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);
}
Maiduguri answered 13/11, 2015 at 5:16 Comment(0)
M
1

// for getting in terms of Decimal/Float

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));
}
Mention answered 21/9, 2016 at 12:8 Comment(0)
A
0

Feel free to use this method I wrote:

int dpToPx(int dp)
{
    return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
}
Anuria answered 10/7, 2013 at 22:2 Comment(3)
What is the purpose of + 0.5f?Smalley
To round up to a whole numberAnuria
Same thing is done in the Android Docs for exampleAnuria
T
0

The answer accepted above is not fully accurate. According to information obtained by inspecting Android source code:

Resources.getDimension() and getDimensionPixelOffset()/getDimensionPixelSize() differ only in that the former returns float while the latter two return the same value rounded to int appropriatelly. For all of them, the return value is in raw pixels.

All three functions are implementedy by calling Resources.getValue() and converting thus obtained TypedValue by calling TypedValue.complexToDimension(), TypedValue.complexToDimensionPixelOffset() and TypedValue.complexToDimensionPixelSize(), respectively.

Therefore, if you want to obtain "raw" value together with the unit specified in XML source, call Resources.getValue() and use methods of the TypedValue class.

Thurnau answered 1/9, 2014 at 16:1 Comment(0)
C
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);

    }
Caterer answered 17/9, 2014 at 10:24 Comment(0)
B
0

variation on ct_robs answer above, if you are using integers, that not only avoids divide by 0 it also produces a usable result on small devices:

in integer calculations involving division for greatest precision multiply first before dividing to reduce truncation effects.

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

5 * 120 = 600 / 160 = 3

instead of

5 * (120 / 160 = 0) = 0

if you want rounded result do this

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

10 * 5 * 120 = 6000 / 160 = 37 + 5 = 42 / 10 = 4
Beatriz answered 31/1, 2015 at 22:52 Comment(0)
R
0

with help of other answers I wrote this function.

public static int convertToPixels(Context context, int nDP)
{
        final float conversionScale = context.getResources().getDisplayMetrics().density; 
        return (int) ((nDP * conversionScale) + 0.5f) ;
}
Rosen answered 5/1, 2016 at 5:36 Comment(0)
B
-1

Here's a other way to do it using kotlin extensions:

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

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

and then it can be used like this from anywhere

12.dpToPx

244.pxToDp
Byrle answered 28/3, 2019 at 19:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.