161

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?

Bram
  • 4,533
  • 6
  • 29
  • 41
  • 1
    Converting dp units to pixel units http://developer.android.com/guide/practices/screens_support.html#dips-pels – Padma Kumar Nov 29 '11 at 11:01
  • @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. – ct_rob Nov 29 '11 at 11:06
  • 1
    I agree with @ct_rob . displayMetrics.densityDpi / 160 minimum value will be 0.75. You must have been casting to int in the incorrect place. – TomTaila Feb 13 '17 at 15:52

21 Answers21

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.

Miha_x64
  • 5,973
  • 1
  • 41
  • 63
Bachi
  • 6,408
  • 4
  • 32
  • 30
  • 13
    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. – nibarius Apr 14 '14 at 14:45
  • @nibarius 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. – Bachi Apr 15 '14 at 21:13
  • Thanks for the clarification, when you need a fixed physical size this is the way to go. – nibarius Apr 16 '14 at 04:17
  • 1
    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: https://groups.google.com/forum/#!topic/android-developers/g56jV0Hora0 – Mark McClelland May 28 '14 at 18:33
  • 1
    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! – william gouvea Jul 09 '14 at 18:52
  • 1
    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://stackoverflow.com/questions/4605527/converting-pixels-to-dp – Rik van Velzen Jul 10 '17 at 13:04
  • why are you hardcoding `displayMetrics.xdpi` ? this answer doesn't use xdpi: https://stackoverflow.com/a/8490361/7767664 – user924 Oct 25 '18 at 13:18
  • this should be easier `(int) ((dp * App.getContext().getResources().getDisplayMetrics().density) + 0.5)` – user924 Oct 25 '18 at 13:20
  • Apparently `return dp * context.getResources().getDisplayMetrics().density;` works – EpicPandaForce Jun 12 '22 at 10:25
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);
Shyam Bhimani
  • 1,310
  • 1
  • 22
  • 37
Bram
  • 4,533
  • 6
  • 29
  • 41
  • 28
    @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. – Kelly Copley Aug 24 '12 at 04:40
  • 3
    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! – Marek Jun 07 '13 at 03:00
  • 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. – Bram Jun 07 '13 at 10:39
  • 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) – Bachi Jul 01 '13 at 17:07
  • 5
    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-pels](http://developer.android.com/guide/practices/screens_support.html#dips-pels) – alocaly Apr 09 '14 at 23:16
  • 1
    +1 Thanks man. Worked like a charm! Thank you for sharing this solution with us. – Simon Dorociak Nov 08 '14 at 20:11
  • Just to clarify that this method will not always give the correct value. Use the accepted answer instead. – いちにち Jun 25 '16 at 10:54
43

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.

Hiren Patel
  • 52,124
  • 21
  • 173
  • 151
  • 6
    Better because there is no need to use Context :) Thanks. – Ayaz Alifov Mar 13 '17 at 16:07
  • 2
    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. – toom Aug 16 '18 at 22:21
  • 1
    nice. should be marked as best answer. kotlin: `private fun dpToPx(dp: Int) = (dp * Resources.getSystem().displayMetrics.density).toInt()` – Raphael C Jan 16 '20 at 14:13
  • Not better, `Resources.getSystem()` is not always correct – EpicPandaForce Jun 12 '22 at 10:22
36

px to dp:

int valueInpx = ...;
int valueInDp= (int) TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, valueInpx , getResources()
                .getDisplayMetrics());
Kannan Suresh
  • 4,573
  • 3
  • 34
  • 59
  • 7
    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 answer – PaNaVTEC Apr 03 '13 at 18:40
  • 1
    @PaNaVTEC, the solution you referenced for dp to px is incorrect. `applyDimension` only yields pixels. See https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/util/TypedValue.java#344 wherein no conversion is performed for `COMPLEX_UNIT_PX`. – Stephen Niedzielski Apr 21 '15 at 02:18
  • 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`. – bitbybit Sep 05 '16 at 13:23
  • ^typo, I meant _"...where you actually need `value / metrics.density`"_ – bitbybit Sep 05 '16 at 13:31
29

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

Aaron Gillion
  • 2,227
  • 3
  • 19
  • 31
pelotasplus
  • 9,852
  • 1
  • 35
  • 37
  • 3
    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. – CJBS Jul 28 '15 at 23:59
14

Use This function

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

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()
Malwinder Singh
  • 6,644
  • 14
  • 65
  • 103
5
px = dp * (dpi / 160)

dp = px * (160 / dpi)
ct_rob
  • 521
  • 3
  • 16
  • 1
    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. – ct_rob Nov 29 '11 at 11:02
  • 2
    `(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)`. –  Nov 29 '11 at 11:06
  • ah, you're right. So all he needs to do is use a long in his formula: 160.0 – ct_rob Nov 30 '11 at 12:51
  • 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. – John smith Nov 21 '17 at 11:25
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.

Parth mehta
  • 1,468
  • 2
  • 23
  • 33
  • nice addition to the given solutions. – Bram Jul 18 '16 at 13:33
  • 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. – Lorne Laliberte Aug 22 '16 at 20:07
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

    .......
}
Antonis Radz
  • 3,036
  • 1
  • 16
  • 34
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.

Paul Lammertsma
  • 37,593
  • 16
  • 136
  • 187
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);
}
Blesson Jose
  • 698
  • 9
  • 14
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));
}
Trinadh Koya
  • 1,089
  • 15
  • 19
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.

Houcine
  • 24,001
  • 13
  • 56
  • 83
1

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

KMI
  • 496
  • 4
  • 24
  • [I prefer this one](http://pixplicity.com/dp-px-converter/) as it includes xxhdpi and xxxhdpi and other units of measure. – Paul Lammertsma Jun 11 '14 at 10:08
  • Link in answer is dead - *"The domain Skinkers.com may be for sale. Click here to inquire about this domain."* – Pang Jun 22 '22 at 01:55
0

Feel free to use this method I wrote:

int dpToPx(int dp)
{
    return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
}
Ben Kane
  • 9,331
  • 6
  • 36
  • 58
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.

Jaromír Adamec
  • 579
  • 4
  • 13
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 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
Adil Soomro
  • 37,609
  • 9
  • 103
  • 153
user2624395
  • 432
  • 1
  • 5
  • 10
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) ;
}
Abhishek
  • 2,295
  • 24
  • 28
-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
Quinn
  • 7,072
  • 7
  • 39
  • 61