What units are applied when declaring a view programmatically? - java

When adding a view in an axml file, it is possible to simply specify the size and the units of the view's attribute, for example:
<TextView
android:TextSize = "10sp"
android:layout_marginTop = "10dp" />
As said in this answer, there are specific units for specific purposes.
My main question is, when applying a size programmatically (by code) in a dynamic way, what are the units applied for the size?
For example, when declaring a TextSize like this:
TextView tv = new TextView();
tv.TextSize = 10;
What are the units applied for the text size? sp? dp? px?
And most importantly, how can I change them to fit my needs?

Hi #Daniel if u programmatically generate textview as following code
TextView tv = new TextView();
tv.setTextSize(10); // Sets text in sp (Scaled Pixel).
And if you want to set text size with other unit so you can achieved by following way.
TextView tv = new TextView();
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, 10); // Sets text in px (Pixel).
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 10); // Sets text in dip (Device Independent Pixels).
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); // Sets text in sp (Scaled Pixel).
tv.setTextSize(TypedValue.COMPLEX_UNIT_PT, 10); // Sets text in pt (Points).
tv.setTextSize(TypedValue.COMPLEX_UNIT_IN, 10); // Sets text in in (inches).
tv.setTextSize(TypedValue.COMPLEX_UNIT_MM, 10); // Sets text in mm (millimeters).
By default Android uses "sp" for text size and "px" for view size.
For other View sizes we can set in px(pixels) but if you want customize the unit you can use custom methods
/**
* Converts dip to px.
*
* #param context - Context of calling class.
* #param dip - Value in dip to convert.
* #return - Converted px value.
*/
public static int convertDipToPixels(Context context, int dip) {
if (context == null)
return 0;
Resources resources = context.getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, resources.getDisplayMetrics());
return (int) px;
}
From above method you can convert YOUR_DESIRED_UNIT in pixels and then set to view. You can replace
TypedValue.COMPLEX_UNIT_DIP
with above unit as per you use case. You can also use it vice-versa for px to dip but we cant assign to custom unit to view so that's why i am using it like this.
I hope i explained well this.

First:
I think you should avoid from set size programmatically as much as possible.
Second:
px
Pixels : corresponds to actual pixels on the screen.
dp or dip
Density-independent Pixels- : an abstract unit that is based on the physical density of the screen. These units are relative to a 160 dpi screen, so one dp is one pixel on a 160 dpi screen
sp
Scale-independent Pixels- : this is like the dp unit, but it is also scaled by the user's font size preference
And in your third question , i think :
for example :
for a edittext you should not use constant for width like this :
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="#string/banklist_firstselectbank"
style="#style/TextAppearanceHeadline2"
android:gravity="center"/>
I think its better to use margin start and margin end like this :
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="#string/banklist_firstselectbank"
style="#style/TextAppearanceHeadline2"
android:layout_marginEnd="50dp"
android:layout_marginStart="50dp"
android:gravity="center"
/>
And use as much as possible fields like : gravity and other instead of constant number.

Related

dynamic EditText different size in different resolutions

for(int l=0;l<c.getCount();l++)
{
EditText etpob = new EditText(this.getActivity());
etpob.setInputType(InputType.TYPE_CLASS_NUMBER);
int po=Integer.parseInt("1" + c.getInt(3)) ;
etpob.setId(po);
etpob.setHint("POB");
etpob.setText("");
etpob.setTextSize(15);
etpob.setVisibility(View.VISIBLE);
etpob.setLayoutParams(new LinearLayout.LayoutParams(0, 50, 15));
etpob.setGravity(Gravity.RIGHT);
}
I have added the EditText dynamically above is my code, But it resolution fails out for different types of screen,in layout using styles we can change it out.
You can use dimension for setting width of dynamic generated edittext
int pixels = 15; // use dimen here
float scale = getContext().getResources().getDisplayMetrics().density;
float dips = pixels / scale;
So,
etpob.setLayoutParams(new LinearLayout.LayoutParams(0, pixels , pixels ));
or if it is require you can set dimen and than you can do it like this way
getContext().getResources().getDimension(R.dimen.activity_horizontal_margin); //set size of dimen in required resolution
You can do the same way.
Define the parameters in style files and use it in code.
Check below link for reference
How to retrieve style attributes programmatically from styles.xml
if you want to use an static integer in your code for size of your EditText, must use values folder in different sizes.
create in res folder these folders: values-small, values-normal,values-large,values-xlarge.
in these folder make a resources file with Integer values. like below
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="edittext_width">70</integer>
<integer name="edittext_height">30</integer>
</resources>
you can set this values with same name but different values in these folders.
then, you must use it in your code:
etpob.setLayoutParams(new LinearLayout.LayoutParams(0, getResources().getInteger(R.integer.edittext_width), getResources().getInteger(R.integer.edittext_height)));
GOOD LUCK.
Incase the editText needs to match_parent
Point size = new Point();
getWindowManager().getDefaultDisplay().getSize(size);
width = size.x;
And set width to EditText
editText.setWidth(width);
etpob.setLayoutParams(new LinearLayout.LayoutParams(0, ConvertPixels(50), ConvertPixels(15)));
public int ConvertPixels(int ht)
{
int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,ht, getActivity().getResources().getDisplayMetrics());
return height;
}
Used existing utility method built called TypedValue.applyDimensions(int, float, DisplayMetrics)

Truly top-aligning text in Android TextView

I am trying to display a TextView in Android such that the text in the view is top-aligned:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create container layout
FrameLayout layout = new FrameLayout(this);
// Create text label
TextView label = new TextView(this);
label.setTextSize(TypedValue.COMPLEX_UNIT_PX, 25); // 25 pixels tall
label.setGravity(Gravity.TOP + Gravity.CENTER); // Align text top-center
label.setPadding(0, 0, 0, 0); // No padding
Rect bounds = new Rect();
label.getPaint().getTextBounds("gdyl!", 0, 5, bounds); // Measure height
label.setText("good day, world! "+bounds.top+" to "+bounds.bottom);
label.setTextColor (0xFF000000); // Black text
label.setBackgroundColor(0xFF00FFFF); // Blue background
// Position text label
FrameLayout.LayoutParams layoutParams =
new FrameLayout.LayoutParams(300, 25, Gravity.LEFT + Gravity.TOP);
// also 25 pixels tall
layoutParams.setMargins(50, 50, 0, 0);
label.setLayoutParams(layoutParams);
// Compose screen
layout.addView(label);
setContentView(layout);
}
This code outputs the following image:
The things to note:
The blue box is 25 pixels tall, just like requested
The text bounds are also reported as 25 pixels tall as requested (6 - (-19) = 25)
The text does not start at the top of the label, but has some padding above it, ignoring setPadding()
This leads to the text being clipped at the bottom, even though the box technically is tall enough
How do I tell the TextView to start the text at the very top of the box?
I have two restrictions to possible answers:
I do need to keep the text top-aligned, though, so if there is some trick with bottom-aligning or centering it vertically instead, I can't use it, since I have scenarios where the TextView is taller than it needs to be.
I'm a bit of a compatibility-freak, so if possible I'd like to stick to calls that were available in the early Android APIs (preferably 1, but definitely no higher than 7).
TextViews use the abstract class android.text.Layout to draw the text on the canvas:
canvas.drawText(buf, start, end, x, lbaseline, paint);
The vertical offset lbaseline is calculated as the bottom of the line minus the font's descent:
int lbottom = getLineTop(i+1);
int lbaseline = lbottom - getLineDescent(i);
The two called functions getLineTop and getLineDescent are abstract, but a simple implementation can be found in BoringLayout (go figure... :), which simply returns its values for mBottom and mDesc. These are calculated in its init method as follows:
if (includepad) {
spacing = metrics.bottom - metrics.top;
} else {
spacing = metrics.descent - metrics.ascent;
}
if (spacingmult != 1 || spacingadd != 0) {
spacing = (int)(spacing * spacingmult + spacingadd + 0.5f);
}
mBottom = spacing;
if (includepad) {
mDesc = spacing + metrics.top;
} else {
mDesc = spacing + metrics.ascent;
}
Here, includepad is a boolean that specifies whether the text should include additional padding to allow for glyphs that extend past the specified ascent. It can be set (as #ggc pointed out) by the TextView's setIncludeFontPadding method.
If includepad is set to true (the default value), the text is positioned with its baseline given by the top-field of the font's metrics. Otherwise the text's baseline is taken from the descent-field.
So, technically, this should mean that all we need to do is to turn off IncludeFontPadding, but unfortunately this yields the following result:
The reason for this is that the font reports -23.2 as its ascent, while the bounding box reports a top-value of -19. I don't know if this is a "bug" in the font or if it's supposed to be like this. Unfortunately the FontMetrics do not provide any value that matches the 19 reported by the bounding box, even if you try to somehow incorporate the reported screen resolution of 240dpi vs. the definition of font points at 72dpi, so there is no "official" way to fix this.
But, of course, the available information can be used to hack a solution. There are two ways to do it:
with IncludeFontPadding left alone, i.e. set to true:
double top = label.getPaint().getFontMetrics().top;
label.setPadding(0, (int) (top - bounds.top - 0.5), 0, 0);
i.e. the vertical padding is set to compensate for the difference in the y-value reported from the text bounds and the font-metric's top-value. Result:
with IncludeFontPadding set to false:
double ascent = label.getPaint().getFontMetrics().ascent;
label.setPadding(0, (int) (ascent - bounds.top - 0.5), 0, 0);
label.setIncludeFontPadding(false);
i.e. the vertical padding is set to compensate for the difference in the y-value reported from the text bounds and the font-metric's ascent-value. Result:
Note that there is nothing magical about setting IncludeFontPadding to false. Both version should work. The reason they yield different results are slightly different rounding errors when the font-metric's floating-point values are converted to integers. It just so happens that in this particular case it looks better with IncludeFontPadding set to false, but for different fonts or font sizes this may be different. It is probably fairly easy to adjust the calculation of the top-padding to yield the same exact rounding errors as the calculation used by BoringLayout. I haven't done this yet since I'll rather use a "bug-free" font instead, but I might add it later if I find some time. Then, it should be truly irrelevant whether IncludeFontPadding is set to false or true.
If your TextView is inside an other layout, make sure to check if there is enough space between them. You can add a padding at the bottom of your parent view and see if you get your full text. It worked for me!
Example: you have a textView inside a FrameLayout but the FrameLayout is too small and is cutting your textView. Add a padding to your FrameLayout to see if it work.
Edit: Change this line
FrameLayout.LayoutParams layoutParams =
new FrameLayout.LayoutParams(300, 25, Gravity.LEFT + Gravity.TOP);
for this line
FrameLayout.LayoutParams layoutParams =
new FrameLayout.LayoutParams(300, 50, Gravity.LEFT + Gravity.TOP);
This will make the box bigger and, by the same way, let enough space for your text to be shown.
OR add this line
label.setIncludeFontPadding(false);
This will remove surrounding font padding and let the text be seen. But the only thing that dont work in your case is that it wont show entirely letters like 'g' that goes under the line... Maybe that you will have to change the size of the box or the text just a little (like by 2-3) if you really want it to work.

How to get height in pixels of LinearLayout with layout_weight

this is my first post. I am learning to code in java for android apps.
I am using a LinearLayout "variable named: ll" with a predefined layout_weight (which I want to change whenever I want in the future) and height and width set to MATCH_PARENT. I want to get the height of this LinearLayout in pixels. I fill this using java (ll.addView(variableForAnObject);).
I am trying to get its height using ll.getLayoutParams().height; but I am getting -1. Because I believe this is the constant for MATCH_PARENT. Setting the height to 0dp makes the ll invisible and gives height as 0.
Please tell me if there's a way to get its height. I think I can do this by getting screen height using DisplayMetrics and making nearly impossible calculations (for me, at least) but this will be too complex.
try with ll.getHeight() should work
if not, it's because android has not yet calculed its bounds, then:
//inside of onCreate method should work
ll.post(new Runnable() {
#Override
public void run() {
//maybe also works height = ll.getLayoutParams().height;
height = ll.getHeight();
}
});
It is -1 because its dimensions have not yet been set, whenever you set layout it takes some time to compute the actual values including height, width etc.
Use OnLayoutListener to listen when it has finished with the layout.
layout.onLayoutChange (View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
//Extract values here
height = view.getHeight();
width = view.getWidth()
}
See this for more about the OnLayoutListener
Define your LinearLayout in your class and use getHeight() and getWidth(). Example:
LinearLayout yourLayout = (LinearLayout) findViewById (R.id.yourLL);
double height = yourLayout.getHeight();
double width = yourLayout.getWidth();
Hope it helps! :)
Give the id field in your layout xml file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:id="#+id/<ID>>
Use this id to get reference in java class and find height and width:
LinearLayout linearLayout= (LinearLayout) findViewById (R.id.<ID>);
double height = linearLayout.getHeight();
double width = linearLayout.getWidth();

How do you use a custom view in a HorizontalScrollView?

I've got a custom view (called CustomStrechView) which extends an ImageView. The point of this view is so that I can get an image to fill the view without losing the aspect ratio.
Now the CustomStrechView works fine when I use it in most applications, but when I try to apply it within my HorizontalScrollView, the images all seem to disappear. I'm not sure why this happens. I've tried removing the <HorizontalScrollView /> and when I do the images show back up, so I feel there is some property that needs to be set, but I can't seem to find it.
Can anyone point out what I’m doing wrong or a better way to get a custom view to work in a HorizontalScrollView?
Edit:
After playing around with this for a bit, I've determined that I can get the custom view to show the image only if I hardcode the width dimension, ie:
<com.example.dragdropshapes.CustomStrechView
android:layout_width="200dp"
Leaving the height as fill_parent is fine...
Code:
Here's how my custom view is being used in the xml file:
<!-- Removing this makes the images show up -->
<HorizontalScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true"
android:layout_marginTop="50dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:showDividers="middle"
android:orientation="horizontal">
<!-- Changing this to a normal ImageView makes the image show up -->
<com.example.dragdropshapes.CustomStrechView
android:id="#+id/pickshapes"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:clickable="true"
android:onClick="pickShapes"
android:background="#drawable/border"
android:src="#drawable/ic_launcher"
/>
<!-- There are 5 other custom ImageViews here -->
</LinearLayout>
</HorizontalScrollView>
Here's the major part of the custom view code:
public class CustomStrechView extends ImageView{
private final Drawable srcPic;
private final String TAG = "strechyTag";
private boolean changeSize = false;
private int Xpx;
private int Ypx;
public CustomStrechView(Context context) {
super(context);
srcPic = this.getDrawable();
}
//... other constructors, not really important...
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
Log.d(TAG, "in the onMeasure!!\n");
int width, height;
width = MeasureSpec.getSize(widthMeasureSpec);
height = width * srcPic.getIntrinsicHeight() / srcPic.getIntrinsicWidth();
setMeasuredDimension(width, height);
}
}
Through some debugging I was able to at least partially solve this myself. Turns out that the width acquired from the MeasureSpec.getSize(widthMeasureSpec); call was 0. Thus the calculation ended in width and height being 0 and the setMeasuredDimension call would then make the image invisible.
Now I'm saying I have this partially solved, because I still don't understand why I was getting that value of 0. I think it's because in a HorizontalScrollView the width isn't determined (it depends on the content within) thus whatever MeasureSpec.getSize(widthMeasureSpec); is actually doing is talking to the parent, and the parent in a normal Linear or RelativeLayout has a defined width where as in a HorizontalScrollView it does not... but that's just speculation at this point.
So I made a fix for it, if the width is 0 we run the calculation via the height instead. The height needed two cases to cover it to avoid divide by 0 errors (the else if and the else collectively).
Here's my current solution with the same xml file I noted in the Question.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if(width > 0)
height = width * srcPic.getIntrinsicHeight() / srcPic.getIntrinsicWidth();
else if(srcPic.getIntrinsicWidth() < srcPic.getIntrinsicHeight())
width = height / (srcPic.getIntrinsicHeight() / srcPic.getIntrinsicWidth());
else
width = height / (srcPic.getIntrinsicWidth() / srcPic.getIntrinsicHeight());
setMeasuredDimension(width, height);
}

How to set unit for Paint.setTextSize()

Is it possible to change the unit for Paint.setTextSize()? As far as I know, it's pixel but I like to set the text size in DIP for multiple screen support.
I know this topic is old and already answered but I would like to also suggest this piece of code:
int MY_DIP_VALUE = 5; //5dp
int pixel= (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
MY_DIP_VALUE, getResources().getDisplayMetrics());
Convert it like this
// The gesture threshold expressed in dip
private static final float GESTURE_THRESHOLD_DIP = 16.0f;
// Convert the dips to pixels
final float scale = getContext().getResources().getDisplayMetrics().density;
mGestureThreshold = (int) (GESTURE_THRESHOLD_DIP * scale + 0.5f);
// Use mGestureThreshold as a distance in pixels
from here http://developer.android.com/guide/practices/screens_support.html#dips-pels
The accepted answer is for gestures, not setting text size. The highest voted answer (at the time of this writing) is close, but the documentation recommends using sp rather than dp because in addition to being scaled for screen densities (as dp values are), sp is also scaled according to user preferred font sizes.
From an int in code
int spSize = 17;
float scaledSizeInPixels = spSize * getResources().getDisplayMetrics().scaledDensity;
mTextPaint.setTextSize(scaledSizeInPixels);
Or alternatively
int spSize = 17;
float scaledSizeInPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spSize, getResources().getDisplayMetrics());
mTextPaint.setTextSize(scaledSizeInPixels);
From resources
Or if you have the sp or dp value in resources:
<resources>
<dimen name="fontSize">17sp</dimen>
</resources>
with
float scaledSizeInPixels = getResources().getDimensionPixelSize(R.dimen.fontSize);
mTextPaint.setTextSize(scaledSizeInPixels);
Other links
How to convert DP, PX, SP among each other, especially DP and SP?
Android: Canvas.drawText() text size on different screen resolutions
Paint.setTextSize
getDimensionPixelSize
And here is even shorter method to convert dp-s to px-els taking display metrics into account
https://developer.android.com/reference/android/content/res/Resources.html#getDimensionPixelSize(int)
If your Paint object is being used to draw text on a Canvas, you can let the Canvas handle scaling for you.
When calling Canvas.drawText() the text size is first determined by the passed in Paint object, which can be set via Paint.setTextSize(). The text size is automatically scaled by Canvas based on the canvas density, which can be found using Canvas.getDensity().
When setting the text size on a paint object that will be drawn on Canvas, work with a unit value of dp or sp and let Canvas handle the scaling for you.

Categories

Resources