I have variable at dimens.xml
<resources>
<dimen name="btn_text_size">12sp</dimen>
</resources>
And i can use it in layout file:
<TextView
android:textSize="#dimen/btn_text_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/dialog_tags_complete"
/>
or programmatically
tagButton.setTextSize(c.getResources().getDimension(R.dimen.tag_text_size));
But this 2 methods give different results. I know that getDimension are based on the current DisplayMetrics associated with the resources.
But what should i do to make this 2 ways looks the same?
setTextSize( float ) expects a scaled pixel value. So, setTextSize( 12 ) would give you the desired result. However, getDimension() and getDimensionPixelSize() return the size in units of pixels, so you need to use the unit-typed variant of setTextSize() as follows:
setTextSize( TypedValue.COMPLEX_UNIT_PX, getDimensionPixelSize( R.dimen.tag_text_size ) );
tagButton.setTextSize(c.getResources().getDimensionPixelSize(R.dimen.tag_text_size));
this will work just fine :)
You should also remember that textView has a setTextSize(int unit,float size), which should be used while setting size from code but not from xml dimen.
I have currently the same thing. Did set a dimension in dimens.xml and applied it programmatically, which is 3 times that big, than when settings via xml.
I checked also:
TextView.getTextSize() = 92f
getResources().getDimension(R.dimen ...) = 92f
TextView.setTextSize(92) != TextView with size from XML, other flags like TypedValue.COMPLEX_UNIT_PX make it even bigger.
The default setTextSize does apply COMPLEX_UNIT_SP by default btw. So once again, the Android API is inconsistent, so setting programmatically only and adapt sizes, so they fit, will be my solution.
Edit: Setting text size programmatically under Galaxy Note 2 (4.4.2) vs Note 4 (5.0.1) leads to a totally different result -.-
Related
When I design a layout, I centralize all dimensions in dimens.xml because of topics of maintainability. My question is if this is correct or not. What would it be the best good practice? There is very little information about this, nothing. I know it's good idea to centralize all strings of a layout on strings.xml, colors on colors.xml. But about dimensions?
For example:
<TableLayout
android:id="#+id/history_detail_rows_submitted"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/cebroker_history_detail_rows_border"
android:collapseColumns="*">
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/history_detail_rows_margin_vertical"
android:background="#color/cebroker_history_detail_rows_background"
android:gravity="center"
android:paddingBottom="#dimen/history_detail_rows_padding_vertical"
android:paddingLeft="#dimen/history_detail_rows_padding_horizontal"
android:paddingRight="#dimen/history_detail_rows_padding_horizontal"
android:paddingTop="#dimen/history_detail_rows_padding_vertical">
<TextView
android:layout_width="match_parent"
android:drawableLeft="#mipmap/ic_history_detail_submitted_by"
android:drawablePadding="#dimen/history_detail_rows_textviews_padding_drawable"
android:gravity="left|center"
android:paddingRight="#dimen/history_detail_rows_textviews_padding"
android:text="#string/history_detail_textview_submitted_by"
android:textColor="#color/cebroker_history_detail_rows_textviews"
android:textSize="#dimen/history_detail_rows_textviews_text_size" />
How to use dimens.xml
Create a new dimens.xml file by right clicking the values folder and choosing New > Values resource file. Write dimens for the name. (You could also call it dimen or dimensions. The name doesn't really matter, only the dimen resource type that it will include.)
Add a dimen name and value.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="my_value">16dp</dimen>
</resources>
Values can be in dp, px, or sp.
Use the value in xml
<TextView
android:padding="#dimen/my_value"
... />
or in code
float sizeInPixels = getResources().getDimension(R.dimen.my_value);
When to use dimens.xml
Thanks to this answer for more ideas.
Reusing values - If you need to use the same dimension multiple places throughout your app (for example, Activity layout padding or a TextView textSize), then using a single dimen value will make it much easier to adjust later. This is the same idea as using styles and themes.
Supporting Multiple Screens - A padding of 8dp might look fine on a phone but terrible on a 10" tablet. You can create multiple dimens.xml to be used with different screens. That way you could do something like set 8dp for the phone and 64dp for the tablet. To create another dimens.xml file, right click your res folder and choose New > Value resource file. (see this answer for details)
Convenient dp to px code conversion - In code you usually need to work with pixel values. However you still have to think about the device density and the conversion is annoying to do programmatically. If you have a constant dp value, you can get it in pixels easy like this for float:
float sizeInPixels = getResources().getDimension(R.dimen.my_value);
or this for int :
int sizeInPixels = getResources().getDimensionPixelSize(R.dimen.my_value);
I give many more details of how to do these things in my fuller answer.
When not to use dimens.xml
Don't put your values in dimens.xml if it is going to make them more difficult to maintain. Generally that will be whenever it doesn't fall into the categories I listed above. Using dimens.xml makes the code harder to read because you have to flip back and forth between two files to see what the actual values are. It's not worth it (in my opinion) for individual Views.
Strings are different. All strings should go in a resource file like strings.xml because almost all strings need to be translated when internationalizing your app. Most dimension values, on the other hand, do not need to change for a different locality. Android Studio seems to support this reasoning. Defining a string directly in the layout xml will give a warning but defining a dp value won't.
add an xml file dimens.xml this is use for support multiple devices.
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="iconarrow">1dp</dimen>
<item name="text_view_padding" type="integer">100</item>
</resources>
then you can use it in your code like this in java code
textview.setPadding(0, 0, 0, getResources().getInteger(R.integer.text_view_padding));
You can also use in other layout(xml file).
android:padding="#dimen/text_view_padding"
you don't need to mention dimen value in value folder file. this library auto manage all the things you just call like that
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/_20sdp"
android:paddingLeft="#dimen/_20sdp"
android:paddingRight="#dimen/_20sdp"
android:paddingTop="#dimen/_20sdp"
android:background="#color/colorPrimary"
android:orientation="vertical"
>
whole code click here for that
But about dimensions?
According to the official Android docs "A dimension is a simple resource that is referenced using the value provided in the name attribute (not the name of the XML file). As such, you can combine dimension resources with other simple resources in the one XML file, under one <resources> element"
For more details refer to http://developer.android.com/guide/topics/resources/more-resources.html
In this post, Devunwired gives three great reasons as to why use dimens.xml When should the dimens.xml file be used in Android?
#Jesús Castro You are doing it right. Maintaining values in the dimens.xml file is better than littering hardcoded values around in all your layout files.
For example, imagine the case where you to increase the left and right margins in all your view. If you used a single value maintained in dimens.xml, this would be a quick change - a single value in a single file.
However, if you had put the margin values as a literal values such as "16dp" in your layout files (instead of using a dimens value like "#dimen/leftright_margin"), you have to go edit each layout file which is error prone and just plain time consuming.
I have a novel method I use which I thought is in keeping with the question. I have been avoiding Xml alot to avoid the cost of parsing xml code.
Rather than using xml dimens ,I use java constants.
either...
public interface DimenConstants { ... }
or...
public class DimenConstants
{
public static void init(Activity activity){...}
}
Then in the case of supporting different screen, you can actually do this yourself in Java at runtime. One way is:
public class TestScreenSizes extends Activity
{
public static final ViewGroup.LayoutParams MAIN_VIEW_SPEC = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
#Override protected void onCreate(Bundle savedState)
{
super.onCreate(savedState);
setContentView(newTextView(),MAIN_VIEW_SPEC);
}
protected TextView newTextView()
{
TextView tv = new TextView(this);
DisplayMetrics display = getResources().getDisplayMetrics();
int resolution = display.widthPixels * display.heightPixels;
if(resolution == 1024) tv.setText("You are using an iphone");
else if(resolution == 4096) tv.setText("You are using a Samsung Galexy");
return rv;
}
}
yes absolutely It is best to keep the values in the dimens.xml file
I don’t know if it can help you but I wrote a little java programe that allows you to duplicate
a dimension xml file with a new desired value so that you no longer have to do it by hand line by line.
https://github.com/Drex-xdev/Dimensions-Scalable-Android
good day,
I was wondering, probably i do something wrong but i don't get it.
i defined
<dimen name="title">16sp</dimen>
When i set it in xml with something like
<TextView android:textSize="#dimen/title"/>
i get a different size (smaller) then when i set it in code.
int mTitleSize = res.getDimension(R.dimen.title);
textView.setTextSize(mTitleSize );
I also tried
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTitleSize);
But that results in the same text size (still biger then the textview in xml.
Does anyone has the same experience/encoutered the same probleem and have a solution (or just a solution)?
Thanks
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)
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.
I want to use some predefined dimensions from my dimens.xml file in my programmatic layout.
So for instance, in my dimens.xml file I have:
<dimen name="margin1">40dip</dimen>
<dimen name="margin2">40dip</dimen>
And then in my programmatic textview I have:
RelativeLayout.LayoutParams lpMargin = new RelativeLayout.LayoutParams( R.dimen.margin1, R.dimen.margin2);
Applying the layout parameter to my textview results in an enormous view.
I already found the problem. If I print out:
Log.e("Metrics", "margin width = " + String.valueOf(R.dimen.margin1));
...it gives an enormous value: margin width = 2131034112. The thing that's messing it up is the units, "dip".
So my question is: what's the correct way to specify dimensions without units for calling programmatically? If that's not possible, can I alter R.dimen.margin1 so I can use it as a programmatic layout parameter?
EDIT: It wasn't the units messing it up -- it's returning the internal ID! (As ianhanniballake says below, I needed to call with getResources().getDimension(R.dimen.margin1)).
Use
getResources().getDimension(R.dimen.margin1)
or
getResources().getDimensionPixelSize(R.dimen.margin1)
Your code is returning the internal ID assigned to 'R.dimen.margin1' rather than the value you assigned in your XML file.