1

On my Pixel 7 device, I want to know how many density-independent pixels (dp) I have. So, I use this formula:

displayMetrics.widthPixels / displayMetrics.density

However, I have an issue because displayMetrics.widthPixels = 1080 (which is okay) and displayMetrics.density = 2.625. When I calculate 1080 / 2.625, it equals 411.428571 density-independent pixels, which is not an integer. It seems that displayMetrics.density = 2.625 may be incorrect because it's unusual to have a fraction of a density-independent pixel.

How can I obtain the correct value for displayMetrics.density? I suppose it's 2.62135922, which would result in 412 density-independent pixels, making it seem more visually appealing.

zeus
  • 12,173
  • 9
  • 63
  • 184

2 Answers2

2

But density actually is a type of float:

public float density

[...] 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.

https://developer.android.com/reference/android/util/DisplayMetrics#density

Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
  • thanks but how can i draw on a 0.4 pixel? also when I look under the browser (chrome) under my pixel 7 it's say I have 412 css pixel width not 411.4. so who is right ? – zeus Apr 23 '23 at 21:37
  • If you come to domain of pixels, which is discrete, then you must end up with integer values. As for Chrome - I do not know what exact CSS property you refer to. – Marcin Orlowski Apr 23 '23 at 21:48
  • yes exactly so how to end up with integer values ? for chrome i just do width = screen.width; in javascript that return me 412 – zeus Apr 23 '23 at 22:00
  • 1
    `screen.width` is and `int`. I do not know where its value comes from but because density is a ratio and is floating point value and screen dimensions are real numbers, then the discrepancy are expected and you just need to account for it. How you do it depends on the application, but probably rounding up and then clipping makes more sense than rounding down and lacking a raster of graphics. But again, when you end up to putting pixels on the screen, you must round at some point, you like it or not :) – Marcin Orlowski Apr 23 '23 at 22:19
  • According to the samples in the docs one should apply a type conversion to Integer on the result. See my post below for details. – Krokomot Apr 25 '23 at 15:39
2

Looking at the Android's official dp-px conversion formula, one can say your calculation as such is correct. And displayMetrics.density is not wrong. The thing is, that

One dp is a virtual pixel unit that's roughly equal to one pixel on a medium-density screen (160 dpi, or the "baseline" density). Android translates this value to the appropriate number of real pixels for each other density.

This definiton of one dp is platform-dependent, e.g. Windows has another one. So your formula is indeed

    px = dp * (dpi / 160)
<=> px = dp * displayMetrics.density
<=> dp = px / displayMetrics.density
<=> dp = displayMetrics.widthPixels / displayMetrics.density

as density is defined as dpi/160.

However, as the holy docs say

Never hardcode this equation

and one should use applyDimension() for the conversion from dp to px. Analogously, deriveDimension() converts px to dp.

Now you say: "Wait a moment, deriveDimension() only comes with Android 14. What shall I do in the meantime?"

Note, that applyDimension() and deriveDimension() both return float values. The docs' example for applyDimension() applies a type conversion to Integer on the result.

So -- keep using your formula for now and simply add a type conversion of the result to Integer.

Krokomot
  • 3,208
  • 2
  • 4
  • 20