344

I'm doing:

button.setLayoutParams(new GridView.LayoutParams(65, 65));

According to the docs the units for the width and height (both 65 in the above) are "pixels". How do you force this to be device independent pixels, or "dp"?

Ken
  • 30,811
  • 34
  • 116
  • 155

7 Answers7

584

You'll have to convert it from dps to pixels using the display scale factor.

final float scale = getContext().getResources().getDisplayMetrics().density;
int pixels = (int) (dps * scale + 0.5f);
Robby Pond
  • 73,164
  • 16
  • 126
  • 119
  • 125
    The correct conversion is (int) (dps * scale + 0.5f). This is the formula we use throughout the framework. – Romain Guy Mar 10 '11 at 04:54
  • 8
    The formula is in the docs. For further reading, go to section 3 of http://developer.android.com/guide/practices/screens_support.html#screen-independence – Ken Mar 20 '11 at 01:49
  • 37
    @RomainGuy, can you please add a utility function to the API to convert from `dp` to `px`? Thanks. – AlikElzin-kilaka May 06 '13 at 14:22
  • @RomainGuy I'm a bit confused. Looking at the source code for `TypedValue.applyDimension()`, for COMPLEX_UNIT_DIP it just returns `value * metrics.density`. Why does your formula have the `0.5f` as well? – telkins May 16 '14 at 15:12
  • 1
    @trevor-e, I believe the `+0.5f` is so the `float` to `int` conversion will round up (and never down) from whatever `dps*scale` yields. However, I could be wrong. – Reed Jun 09 '14 at 21:56
  • 3
    what is dps here? im getting "dps cannot be resolved to a variable" error? what type of "dps" need to declare?? – Deepak Aug 11 '14 at 11:18
  • 5
    The "dps" variable is the input value that you want to convert. – Robby Pond Sep 26 '14 at 17:25
  • Totally worked. This answer get correct dp value. When I got integer value, depend on the scale value, I can get correct dp value. – Huy Tower Sep 25 '15 at 03:42
  • 1
    Great answer, totally worked, I just wonder whats the reason of the 0.5f – Jesus Almaral - Hackaprende Oct 05 '15 at 19:24
  • 3
    Regarding the `+0.5f`, the purpose is to round to the nearest pixel. If left off, the number of pixels will always be rounded down since casting to `int` truncates/floors the input. Here are some examples to illustrate this: `round(3) = floor(3 + .5) = floor(3.5) = 3`, `round(2.5) = floor(2.5 + .5) = floor(3) = 3`, `round(2.49) = floor(2.49 + .5) = floor(2.99) = 2` – MarredCheese Jan 11 '18 at 05:43
  • 1
    Why we are adding 0.5f ? – Bhaven Shah Aug 04 '20 at 06:21
  • **Kotlin: context.resources.displayMetrics.density** – J A S K I E R Mar 25 '21 at 12:34
275

I know this is an old question however I've found a much neater way of doing this conversion.

Java

TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 65, getResources().getDisplayMetrics());

Kotlin

TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 65f, resources.displayMetrics)
Cassie
  • 5,223
  • 3
  • 22
  • 34
  • 9
    Well done for finding out how to do this. I'm not a fan of repeating a formula in several places! Thanks. – darrenp Nov 25 '13 at 13:49
  • 2
    If not, then create function with the formula – Abdul Saleem Oct 09 '15 at 14:29
  • 1
    @Kenobi It does the conversion for you from DP to PX. The `65` above is the DP value you want converted to PX – Cassie May 29 '16 at 02:08
  • 9
    This should be the accepted answer, not the current one where these number (0.5) coming magically from no where – Johny19 Jul 03 '17 at 22:03
  • 3
    @Johny19 The 0.5 is not magic. It's just how to round a floating point number to the nearest integer instead of truncating it. See my comment on Robby Pond's answer for more detail. – MarredCheese Jan 11 '18 at 05:47
  • this is the cleanest solution and should be considered the best answer – save_jeff Mar 07 '23 at 08:06
46

simplest way(and even works from api 1) that tested is:

getResources().getDimensionPixelSize(R.dimen.example_dimen);

From documentations:

Retrieve a dimensional for a particular resource ID for use as a size in raw pixels. This is the same as getDimension(int), 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.

Yes it rounding the value but it's not very bad(just in odd values on hdpi and ldpi devices need to add a little value when ldpi is not very common) I tested in a xxhdpi device that converts 4dp to 16(pixels) and that is true.

Gowthaman M
  • 8,057
  • 8
  • 35
  • 54
David
  • 2,129
  • 25
  • 34
44

Looking at your requirement, there is alternate solution as well. It seems you know the dimensions in dp at compile time, so you can add a dimen entry in the resources. Then you can query the dimen entry and it will be automatically converted to pixels in this call:

final float inPixels= mActivity.getResources().getDimension(R.dimen.dimen_entry_in_dp);

And your dimens.xml will have:

<dimen name="dimen_entry_in_dp">72dp</dimen>

Extending this idea, you can simply store the value of 1dp or 1sp as a dimen entry and query the value and use it as a multiplier. Using this approach you will insulate the code from the math stuff and rely on the library to perform the calculations.

vine'th
  • 4,890
  • 2
  • 27
  • 27
7

Based on drspaceboo's solution, with Kotlin you can use an extension to convert Float to dips more easily.

fun Float.toDips() =
        TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, resources.displayMetrics);

Usage:

(65f).toDips()
RickSanchez725
  • 336
  • 1
  • 6
  • 14
0

Kotlin Version

val scale: Float = resources.displayMetrics.density
val resizedInDp = (stream.videoWidth * scale + 0.5f).toInt()

Usage:-

val params: ViewGroup.LayoutParams = yourLayout!!.layoutParams
            val scale: Float = resources.displayMetrics.density
            params.width = (widthDp * scale + 0.5f).toInt() // dp to px
            params.height =
                (heightDp * scale + 0.5f).toInt() // setting height according to aspect ratio
            yourLayout!!.layoutParams = params
Quick learner
  • 10,632
  • 4
  • 45
  • 55
0

Simply you can use androidx.annotation

@Dimension(unit = Dimension.DP)
private static final int strokeWidth = 1; 
Mohamed Slama
  • 189
  • 1
  • 3
  • 17