getDimension()/getDimensionPixelSize() - mutliplier issue
Asked Answered
L

5

21

So I have android 2.3.5 device which is NORMAL/HDPI. I have a dimens.xml in my project:

...
    <dimen name="gameresult_congrats_label_msg_textSize">20sp</dimen>
...

this file is absolutely identical in values-normal/values-hdpi and so on folders. In my first activity app shows me that value using:

Toast.makeText(this, "textSize is "+getResources().getDimensionPixelSize(R.dimen.gameresult_congrats_label_msg_textSize), Toast.LENGTH_SHORT).show();

and it displays 30. I Tried also:

Toast.makeText(this, "textSize is "+getResources().getDimension(R.dimen.gameresult_congrats_label_msg_textSize), Toast.LENGTH_SHORT).show();

but result is the same. But only when I tried this:

Toast.makeText(this, "textSize is "+getResources().getString(R.dimen.gameresult_congrats_label_msg_textSize), Toast.LENGTH_SHORT).show();

I got my "20sp" finally! But why is that? Official docs says that those methods returns

Resource dimension value multiplied by the appropriate metric.

I checked this by changing my value to 25 and I got 38 which means aos uses 1.5 multiplier. But why? It already gets value from appropriate folder which means it gets a ready to use value! From where aos gets that 1.5x multiplier? I know it depends on DisplayMetrics. But how it calculates 1.5x?
UPDATE
I understand about multiplier but, you see, the real problem here is about double scaling. And thats why I did asked this question.
So if I have some layout.xml (in res\layout folder) with TexView defined like:

<TextView
    android:id="@+id/congratsLabel"
    ...
    android:textSize="@dimen/gameresult_congrats_label_msg_textSize" />

Everything looks ok. I mean textview is like Im expecting.
Now lets do the same in code:

TextView congratsLabel = fineViewById(R.id.congratsLabel); 
textSize = getResources().getDimension(R.dimen.gameresult_congrats_label_msg_textSize)
congratsLabel.setTextSize(textSize) 

and here is the issue!!! getResources().getDimension() returns a SCALED value and thats ok. But the resulting size of my textView will be 1.5 greater than I expecting cuz setTextSize works with SP and here comes the second scale! And thats why AOS makes resulting text size scaled to 45 (originally defined as 20sp).

Lavinalavine answered 3/5, 2013 at 23:23 Comment(1)
setTextSize() takes an additional int parameter that specified the type of the float size. For example, for already scaled size value, use TypedValue.COMPLEX_UNIT_PX.Eadie
C
16

Per the Supporting Different Screen Densities training, hdpi is 1.5x normal (mdpi) sizes. As getDimensionPixelSize takes this difference into account when converting into pixels, the returned value will be 1.5x your value in sp.

Note that sp is also dependent on the user's preferred text size and can therefore change to be even larger than 1.5x your expected value.

Cyndycynera answered 3/5, 2013 at 23:33 Comment(5)
Does aos do it no matter if it uses corresponding values folder? Like if aos gets the value from values-hdpi why it still multiplies it by 1.5 to get the HDPI value? Or if there is a dimens.xml do exist in values-hdpi why not to use it without any post-conversion?Lavinalavine
@Lavinalavine - dimensions in dp or sp are always scaled when converting to pixels (that's what they are there for). This ensures that the physical size of your text is exactly the same on each device, no matter the screen density (20px on mdpi in physical inches==30px on hdpi in physical inches). You can, of course, build your dimensions in pixels (i.e., 20px), but that will mean your text is extremely small on xhdpi devices while huge on ldpi devices, which almost never what you want.Cyndycynera
Yes but why does getDimension() do this? I just wanted only one thing: to get my dimension value as it is. Like if its 20sp I expect to get 20 instead of 20x1.5 pixels. Because I want it to use in method like view.setTextSize() which accepts values treated as SP instead of pixels. And that is the issue. Cuz then I use 20 in this method everything looks fine but when I'm trying to get the value using getDimension (not getDimensionPixelSize) and AOS returns 30 the text becomes too big.Lavinalavine
Maybe my problem is I thought that getResources().getDimension() returns ORIGINAL value from dimension-files but it returns PIXEL-SIZED (like measuring) values instead of original values.Lavinalavine
Old post but a side point - setTextSize doesn't accept values treated as SP. If you use setTextSize(TypedValue.COMPLEX_TYPE_PX, textSize) then it will work.Fore
M
28

Just to clarify (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 appropriately. For all of them, the return value is in raw pixels.

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

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

Medicaid answered 1/9, 2014 at 15:55 Comment(0)
N
19

Method getDimension() converts dp or sp values into pixels based on current screen density. This is very useful as you don't have to do it on your own, when you want to set in Java width or text size (they accepts only pixels).

But if you need original sp or dp you could do "reverse engineering".

Step 1. Check current scale ratio (based on screen density):

float scaleRatio = getResources().getDisplayMetrics().density;

Step 2. Get dimension from dimens.xml

float dimenPix = getResources().getDimension(R.dimen.your_dimen_name);

Step 3. Do some math

float dimenOrginal = dimenPix/scaleRatio;

Remarks:

  • usually you need int for dimension methods (like setWidth()), so you have to convert float result to int for instance using Math.round()
  • more accurate result when rounding to int you could get using such formula (dimenPix-0.5f)/scaleRatio
  • in case of sp you could take also into account user preferences about text scale

Read more about dimensions in Android: http://android4beginners.com/2013/07/appendix-c-everything-about-sizes-and-dimensions-in-android/

Neela answered 6/7, 2013 at 11:49 Comment(1)
You see, the problem here is about double scaling. If I create some label in code and I wish to use defined size for its text I use something like textSize = getResources().getDimension(R.dimen.gameresult_congrats_label_msg_textSize) and this returns a SCALED value. Now if set it using myLabel.setTextSize(textSize) the resulting size will be 1.5 greater than I need cuz setTextSize works with SP and here comes the second scale! And thats why I wanted to get the original size (20) instead of scaled (30) which makes resulting text size once again scaled to (45)Lavinalavine
C
16

Per the Supporting Different Screen Densities training, hdpi is 1.5x normal (mdpi) sizes. As getDimensionPixelSize takes this difference into account when converting into pixels, the returned value will be 1.5x your value in sp.

Note that sp is also dependent on the user's preferred text size and can therefore change to be even larger than 1.5x your expected value.

Cyndycynera answered 3/5, 2013 at 23:33 Comment(5)
Does aos do it no matter if it uses corresponding values folder? Like if aos gets the value from values-hdpi why it still multiplies it by 1.5 to get the HDPI value? Or if there is a dimens.xml do exist in values-hdpi why not to use it without any post-conversion?Lavinalavine
@Lavinalavine - dimensions in dp or sp are always scaled when converting to pixels (that's what they are there for). This ensures that the physical size of your text is exactly the same on each device, no matter the screen density (20px on mdpi in physical inches==30px on hdpi in physical inches). You can, of course, build your dimensions in pixels (i.e., 20px), but that will mean your text is extremely small on xhdpi devices while huge on ldpi devices, which almost never what you want.Cyndycynera
Yes but why does getDimension() do this? I just wanted only one thing: to get my dimension value as it is. Like if its 20sp I expect to get 20 instead of 20x1.5 pixels. Because I want it to use in method like view.setTextSize() which accepts values treated as SP instead of pixels. And that is the issue. Cuz then I use 20 in this method everything looks fine but when I'm trying to get the value using getDimension (not getDimensionPixelSize) and AOS returns 30 the text becomes too big.Lavinalavine
Maybe my problem is I thought that getResources().getDimension() returns ORIGINAL value from dimension-files but it returns PIXEL-SIZED (like measuring) values instead of original values.Lavinalavine
Old post but a side point - setTextSize doesn't accept values treated as SP. If you use setTextSize(TypedValue.COMPLEX_TYPE_PX, textSize) then it will work.Fore
A
8

If someone else needs this :

To address the double scaling problem Stan show when using getDimensionPixelSize with TextView.setTextSize :

You can use the alternate version of setTextSize where you can specify the unit like this :

title.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen.title));
Abmho answered 9/4, 2014 at 13:23 Comment(1)
this was very useful when i have to set a static dimension from javaHermeneutics
D
1

Because... NORMAL is NOT hdpi...
Normal is mdpi (160dpi) = 1.0x.
hdpi (240dpi) is 1.5x.
xhdpi (320dpi) is 2.0x.
xxdpi (480dpi) is 3.0x.
xxxhdpi (640dpi) is 4.0x.
And (last, but not least) ldpi (120dpi) is 0.75x.

Dorcasdorcea answered 14/10, 2013 at 16:8 Comment(4)
You see, the problem here is about double scaling. If I create some label in code and I wish to use defined size for its text I use something like textSize = getResources().getDimension(R.dimen.gameresult_congrats_label_msg_textSize) and this returns a SCALED value. Now if set it using myLabel.setTextSize(textSize) the resulting size will be 1.5 greater than I need cuz setTextSize works with SP and here comes the second scale!Lavinalavine
So... I normally use sp and don't even think to manually scale.Clammy
OR you perform YOUR scaling (in spite of system scaling) and just use px instead of sp. You'll get some Lint warnings, of course but you can just ignore them.Clammy
The point is about reusing dimens.xml. In some layouts Im using some text size (declared in dimens.xml) for Labels but at the same time I was need to create some Label programaticaly. So I wanted to use the same font size as declared in dimens.xml. And here comes the problem about double scaling.Lavinalavine

© 2022 - 2024 — McMap. All rights reserved.