GetDpi()

Monkey Targets Forums/Android/GetDpi()

Midimaster(Posted 2013) [#1]
This functions returning the group and real Dpi of the display. With this you could f.e. calculate the real (millimeter) width of pixel areas on the screen:

I tested this on V69 and Android 2.3 and the sample below works:

save this as "testdpi.java"


The sample app:



The difference is:
The first function return the common used dpi value of the group of similiar quality displays, this diplay belongs to. Normaly return 120 oder 160 or on high density display 240. This value is good enough for arranging elements on your screen.

120 means: this smartphone belong to the group of smartphones with roundabout 120dpi displays, but 120 pixel will not exatly be 25,4 mm!


The second value descripes exactly the real dpi of this device. f.e. 123.2
Use this value in combination with a pixel value to know exactly millimeters.

123.2 means: this smartphone has a display with 123.2dpi, and 123.2 pixel will exatly be 25,4 mm!


there are some more possible values you could reach with varying this function:


INT:

densityDpi
-> The screen density expressed as dots-per-inch.

heightPixels
-> The absolute height of the display in pixels.

widthPixels
-> The absolute width of the display in pixels.





FLOAT:
density
-> The logical density of the display.

scaledDensity
-> A scaling factor for fonts displayed on the display.

xdpi
-> The exact physical pixels per inch of the screen in the X dimension.

ydpi
-> The exact physical pixels per inch of the screen in the Y dimension.


ElectricBoogaloo(Posted 2013) [#2]
Handy! .. I think.
It's one of those "if I could think of a purpose" things,where I'm not entirely sure how useful it is until I can think of a reason to use it, but then it'd be invaluable!
Thanks for posting this.

(Maybe good to reformat GUI/buttons when displayed on a teensy tiny screen!)


Midimaster(Posted 2013) [#3]
Purpose:
If you have in-game GUI or like me a piano keyboard, you need to know how big the buttons are in real. Is it big enough for the fingers of the user? Or look they too big and ugly on big devices like the nexus?


DGuy(Posted 2013) [#4]
@ElectricBoogaloo:
By expressing dimensions in device-independent-pixels (DP) then converting them to physical pixels based upon the screens' DPI, an image that is, say, 1 inch wide (~2.5 cm) on one device will be 1 inch wide on every device.

For a good explanation, see this article Supporting Multiple Screens. In it is shown how to use the 'DisplayMetrics.density' property to do the conversion.


Supertino(Posted 2013) [#5]
Very handy thanks.


Gerry Quinn(Posted 2013) [#6]
It could be pretty useful; for example you could make buttons bigger on tiny screens rather than scaling them to be the same relative size on every screen with the same pixel size.


Supertino(Posted 2013) [#7]
I am using it gauge how far some one has moved their finger(s), i.e a user has to move their finger over at least 5% of the screen before the action is recorded.


Supertino(Posted 2013) [#8]
Word of caution when using the xdpi and ydpi methods, these are not reliable, the fields are not populated from the hardware but from the manufacturer of the device, most manufactures don't put the correct values in the xdpi or ydpi fields, my S3 mini and Nexus 7 return incorrect results so best not to use them.

In-fact none of the getDisplayMetrics() fields are populated by hardware and are all defined by the manufacture.

The only field you can rely on is the density field which will give you a scale value from a base dpi of 160, but again the value this returns is an approximation and is entered by the manufacturer and should not be used to calculate the true DPI. Sadly there appears to not be a reliable way of returning the true width, height, diagonal of a screen in inches. The best you can come up with is an approximation for LOW,MED,HIGH,XHIGH Density.

I found this out the hard way last night when trying to create a fancy method to return all this so I can scale my UI etc.


Midimaster(Posted 2013) [#9]
I know that the values are not 100% exact.... But are they so bad, that you cannot use them? I think, The approximation LOW to XHIGH is less realistic then the xdpi...


Supertino(Posted 2013) [#10]
My post was more to alert users who might be using these methods to create rulers or other things that must on all devices be a set real world width, but might be under the impressing that xdpi,ydpi are *always* correct. The emulator always returns correct results but real devices will in most cases not.

In my testing my S3 mini;

=INCHES=
        | Real |  Returned from xdpi,ydpi  | differnce
        | ---- |  ------------------------ | 
width   | 2    | 2.99                      | +33%
height  | 3.4  | 5.00                      | +32%



As you can see it's way off.


Midimaster(Posted 2013) [#11]
Wow! This is a lot,... I have to re-think about it...

Can you please write me, what is the returned GetAndroid_XDpi() value?

And what is returned by GetAndroid_Dpi()?


Supertino(Posted 2013) [#12]
Sure I'll get the info I get from my Nexus 7 and S3 mini.

I have been reading this blog http://blogs.captechconsulting.com/blog/steven-byle/understanding-density-independence-android which explains how we should be doing it. Butt here is plenty of talk around the interwebz about dpi issue not being reliable.

I am going to implement some DP method tonight and see how I get on.


Supertino(Posted 2013) [#13]
Galaxy S3 mini (real device)
Actual
======
Diagonal = 4.0
Width = 2.0
Height 3.5
DPI = 233

Returned
========
Diagonal = 5.82
Width (xdpi) = 2.99
Height (ydpi)= 5.0
DPI = 240


Nexus 7 (real device)
Actual
======
Diagonal = 7.0
Width = 3.7
Height 5.94
DPI = 216

Returned
========
Diagonal = 7.26
Width (xdpi) = 4.09
Height (ydpi)= 6.0
DPI = 213


Using the following;

Import mojo
Import "dpi.java"

Extern
	#If TARGET="android"
		Function GetAndroidDpi:Float() = "DPIStuff.GetAndroid_Dpi"
		Function GetAndroid_LogicalDpi:Float() = "DPIStuff.GetAndroid_LogicalDpi"
		Function GetAndroidDpi_x:Float() = "DPIStuff.GetAndroid_XDpi"
		Function GetAndroidDpi_y:Float() = "DPIStuff.GetAndroid_YDpi"
		Function GetAndroid_heightPixels:Int() = "DPIStuff.GetAndroid_heightPixels"
		Function GetAndroid_widthPixels:Int() = "DPIStuff.GetAndroid_widthPixels"
	#Endif
Public

Function ANDROID_GetLogicalDPI:Float()
	#If TARGET<>"android"
	Return 0
	#ELSE
	Return GetAndroid_LogicalDpi
	#ENDIF
End Function

Function ANDROID_heightPixels:Int()
	#If TARGET<>"android"
	Return 0
	#ELSE
	Return GetAndroid_heightPixels
	#ENDIF
End Function

Function ANDROID_widthPixels:Int()
	#If TARGET<>"android"
	Return 0
	#ELSE
	Return GetAndroid_widthPixels
	#ENDIF
End Function

Function ANDROID_GetDpi:Float()
	#If TARGET<>"android"
	Return 0
	#ELSE
	Return GetAndroidDpi
	#ENDIF
End Function

Function ANDROID_GetDiagonalInches:Float()
	#If TARGET<>"android"
	Return 0
	#ELSE
	Local width:Float = (GetAndroid_widthPixels / GetAndroidDpi_x()) * (GetAndroid_widthPixels / GetAndroidDpi_x())
	Local height:Float = (GetAndroid_heightPixels / GetAndroidDpi_y()) * (GetAndroid_heightPixels / GetAndroidDpi_y())
	Return Sqrt(width + height)
	#ENDIF
End Function

Function ANDROID_GetWidthInches:Float()
	#If TARGET<>"android"
		Return 0
	#Else
	Return (GetAndroid_widthPixels / GetAndroidDpi_x)
	#ENDIF
End Function

Function ANDROID_GetHeightInches:Float()
	#If TARGET<>"android"
		Return 0
	#Else
	Return (GetAndroid_heightPixels / GetAndroidDpi_y)
	#ENDIF
End Function


and

class DPIStuff {
	public static int GetAndroid_Dpi() {
		DisplayMetrics metrics = new DisplayMetrics();
		BBAndroidGame._androidGame._activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
		int dpi= metrics.densityDpi;
		return dpi;
	}
	
	public static float GetAndroid_LogicalDpi() {
		DisplayMetrics metrics = new DisplayMetrics();
		BBAndroidGame._androidGame._activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
		float ldpi= metrics.density;
		return ldpi;
	}
	
	public static float GetAndroid_XDpi() {
		DisplayMetrics metrics = new DisplayMetrics();
		BBAndroidGame._androidGame._activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
		float dpi= metrics.xdpi ;
		return dpi;
	}
	
	public static float GetAndroid_YDpi() {
		DisplayMetrics metrics = new DisplayMetrics();
		BBAndroidGame._androidGame._activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
		float dpi= metrics.ydpi ;
		return dpi;
	}
	
	public static int GetAndroid_heightPixels() {
		DisplayMetrics metrics = new DisplayMetrics();
		BBAndroidGame._androidGame._activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
		int height= metrics.heightPixels ;
		return height;
	}
	
	public static int GetAndroid_widthPixels() {
		DisplayMetrics metrics = new DisplayMetrics();
		BBAndroidGame._androidGame._activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
		int width= metrics.widthPixels ;
		return width;
	}
}



Supertino(Posted 2013) [#14]
The following seems to do the job.

Function actualmm:Float(millimeters:Float)
	Local mmPerInch:Float = 25.39
	Return convertDpToPixel( (160 / mmPerInch) * millimeters)
End Function

Function convertDpToPixel(dp:Float)
	Return Ceil(dp * (ANDROID_GetDpi / 160))
End Function

Function convertPixelToDp(px:Float)
	Return Ceil(px / (ANDROID_GetDpi / 160))
End Function


DrawRect 100, 400, actualmm(25), actualmm(25) ' Draws a 1 ~inch square on both my Nexus 7 and S3
DrawRect 300, 400, actualmm(10), actualmm(10) ' Draws a 10mm square on my Nexus 7 and S3


Also now that we know how many pixels make up an Inch we can better determine the screens width and hight in inches, but again it wont be 100% more like with 5% tolerance.

Local OneInch:Float = 25.39
Local width:Float = Float(DeviceWidth) / actualmm(OneInch)
Local height:Float = Float(DeviceHeight) / actualmm(OneInch)


I get the following values using this new method;

Galaxy S3 mini (real device)
Actual
======
Width = 2.0
Height = 3.5

Returned (new)
========
Width = 2.0
Height = 3.33

Returned (old)
========
Width (xdpi) = 2.99
Height (ydpi)= 5.0


Nexus 7 (real device)
Actual
======
Width = 3.7
Height = 5.94

Returned (new)
========
Width  = 3.7
Height = 5.66

Returned (old)
========
Width (xdpi) = 4.09
Height (ydpi)= 6.0




Midimaster(Posted 2013) [#15]
I still believe in some calculation errors in your post...

How can calculations based on metrics.densityDpi return correct values, when metrics.densityDpi only knows three values 120 160 and 240 for all devices on the world? This stepped values can cause variation errors upto 20%

Couldn't it be a mere change in case of your S3 mini, because its real dpi are very closed (accidentally) to 240dpi?


How did you meter the 2.0inch of your device? On internet fotos of the S3 mini I'm metering 63mm width of the device and 58mm width of its screen. This are not 2 inch but 2.3 inch???

this would mean:

480 pix / 2.3 inch = 208dpi


You wrote a lot, but still you did not tell me, what the android function metrics.xdpi really returned on your S3 mini....

because what you wrote:
Width (xdpi) = 2.99

cannot be returned truely! This is a inch value and not a dpi value.


Supertino(Posted 2013) [#16]
Ah sorry yes I was converting into inches here the returned values from xdpi an ydpi (not additional calculations applied), also the dpi and logical dpi (scale) again no additional calculations.

S3 MINI

xdpi = 160.42 
ydpi = 160.00
dpi = 240.0 
logical dpi = 1.5


Tape measure floats a little above the screen - when pressed down its bang on 1 inch, it's more precise cus it's fall's on the 240 bucket values.


NEXUS 7
xdpi = 195.38
ydpi = 200.69
dpi = 213.0
logical dpi (scale from 160) = 1.33


Tape measure floats a little above the screen when pressed down its 1mm more than 1 inch, it's off a little as the dpi does not fall onto one of the bucket values.


I don't use the bucket dpi value (120,160,240 etc) I use the value got from the dpi method which return 213 on my nexus 7 and 240 on my S3, this is more accurate than the bucket value on the case of the nexus 7, reading forums it's seems safe to use this value, if i did use the bucket value then my s3 would be bang on 1 inch as it falls directly on the 240 value but my nexus 7 being 213-216 would be way off by several mm's if it's aligned to a bucket value of say 240.

It would probably be an idea to put in some code that would use values based on a bucket value if the returned dpi is way off the calculated bucket value.... (makes sense to me honest)

As to the actual screen sizes - when I measure them more precisely with my tape measure they are as follows;

S3 Mini
width = 55mm
height = 88mm


Nexus 7
width = 94mm
height = 143mm (excluding the softkey button row)


Ready this whole thread through again I can see it being quite confusing - I think tonight I'll put all of this into a run-able example and get the community to test it out on various devices.

But using xdpi() and ydpi() is a big'ol No! No! that is a sure thing.


Midimaster(Posted 2013) [#17]
ok.. I see the returned metrics.xdpi of 160 is completely far away from the reality. It looks like the manufactors do not care about the correct value of each device when setting up a new device model os...

If I understood you right the metrics.densityDpi seems to return the best value. 213 on nexus would be very closed to the real dpi, but 240 on the S3 looks like beeing far away form it. But your metering shows, that it is good enough.

thank you for spending your time. This is really helpful.


Supertino(Posted 2013) [#18]
Well the S3 mini is 233 according to most sources so it's not far off.