Why is my TextView padding unpredictable in pixels - android

I have extended the TextView and added support for borders, the thing is when I am drawing a border I need to put padding on the bordered side, so that the text would move.
I set my widths of borders in pixels, and it draws them accordingly, but on my TF201 tablet when I setPadding on the TextView, out of some reason it multiplies the padding width by 3x in pixels even though the setpadding documentation says it is defined explicitly in pixels.
EDIT:
Even though the answer I have selected is not what was causing my issue, it is a valid answer. The real answer to my question is actually a duplicate from this. Problem was that I have added a value to my padding each time setPadding was called. And it does get called three times on a page that has scrolling to it.

It might be a issue of pixel density. Its true that setpadding docs asks to set the padding in pixels but are you setting it in px, sp or dp ? If you read Supporting Different Densities document it says and I quote:
Different screens have different pixel densities,so the same number of pixels may correspond to different physical sizes on different devices.
So, when you specify spacing between two views, use dp rather than px:
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/clickme"
android:layout_marginTop="20dp" />
When specifying text size, always use sp:
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp" />
Also, based on your comments:
drawRect unit issues android andDraw Rectangle which change size w.r.t different android screen size question might help.

While the method may only accept pixel values, that sadly doesn't save you from needing to take screen densities into account. Instead, you need to determine your values in terms of DP and then programmatically calculate the pixel equivalents at runtime. Fortunately, there are some built-in methods to help you out. This can be done with the following code:
/// Converts 14 dip into its equivalent px
int dimensionInDp = 14;
Resources r = getResources();
float dimensionInPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dimensionInDp, r.getDisplayMetrics());
Although the result is a float, you can easily cast it to an int for use in your setPadding(...) method.
(Referencing: Converting pixels to dp)

Related

what is fadingEdgeLength attribute meant for?

what is the use of android:fadingEdgeLength attribute meant for. i have seen this attribute for some views and wigdets in android project's xml
It's used for adjusting the length of the fading edge that you see when you scroll. You can see the effects on the edges of the screen in the picture below.
From the android official documentation:
Defines the length of the fading edges.
Must be a dimension value, which is a floating point number appended with a unit such as "14.5sp". Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size), in (inches), mm (millimeters).
The fadingEdgeLengthmanages the size of fading shown at edges of a View.
For instance, a ScrollView or a ListView.
You can combine threquiresFadingEdge to show it horizontally or vertically or none (the latter, in order not to show any).

Why dpToPixel should not be used to set layout dimensions?

Why in javadocs for dpToPixel (declared here) is stated that it shouldn't be used to set layout dimensions?
We don't use pixels to define layout dimensions. Using density independent pixels ensures proper layout sizes across a variety of devices. Meaning said layout will have roughly the same visual size on a 4" phone and a 7" tablet. (Which is completely unrelated to the problem at hand :) )
That being said the actual layout sizes (e.g. LayoutParams class) are in fact using whole pixels to define the resulting size. It is viable to use the dpToPixels method in this way:
float px = dpToPixels(16, getResources()); // 16 dp to pixels precise
int pxOffset = (int) px; // 16dp rounded down to whole pixels
int pxSize = (int) (0.5f + px); // 16dp rounded up to whole pixels
Now you can use these values in layouts, pxOffset for padding, margin etc. (can be zero) and pxSize for width and height (ensures at least 1px size unless zero). The same calculation is done when using methods int Resources.getDimensionPixelOffset(int) and int Resources.getDimensionPixelSize(int) which are suitable for use with layouts and float Resources.getDimension(int) which is suitable for drawing precisely.
EDIT
Elevation uses float values so using the precise dimension is completely fine.
TranslationX, translationY or translationZ are defined using float values. These and many more view properties are used for animation so it makes sense to use smooth values. If set by hand use whole integers for predictable behavior.

getDimension()/getDimensionPixelSize() - mutliplier issue

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).
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.
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/
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.
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));
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.

Force width when generating dynamic linearlayouts

I'm setting up a layout, that I'm adding to (via a for-loop) based on a set of objects. The issue is if there are say 3 fields (columns), if the middle field has a longer text length in the first entry than the second... the middle field in the first one will consume a larger width, even if I've already defined a weightSum and a layout_weight for that center column. My question is, when dynamically adding rows like this, how can I ensure that they all end up being the exact same width, as defined in the XML?
Why don't you just set a fixed width?
As in setWidth(int width)
EDIT:
The formula to convert px to dpi is:
truePixels = DIPs * (device DPI / 160)
Here take a look how to get screen dpi.
Docs about supporting multiple screens.
If you're using xml layout for rows then:
android:layout_width="0"
If everything is done programmatically then setWidth(0)

Android text shadow units

Android text shadows have shadowDx and shadowDy to specify the shadow's offset. These are floats and are a factor rather than absolute units. The answer given here implies that there's no easy way of specifying the shadow's position in pixels or dips: TextView:shadowDx/Dy/Radius in dip?
So... what do the units mean? If I give a shadowDx of 1.5, that's 1.5 what? 1.5 times the text size?
OK, I guess the units are physical pixels. The fact that the documentation doesn't state what the units are (it just says "Must be a floating point value"), and that you don't get to choose which units (pixels or dip), was confusing me.
Actually, I think the unit is pd, not pixels.

Categories

Resources