How to align the text to top of TextView? - android

How to align the text to top of a TextView?
Equivalent Android API for Swings setInsets()?
that is top of text should start be in (0,0) of TextView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:id="#+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="42sp"
android:paddingTop="0px"
android:gravity="top">
</TextView>
</LinearLayout>
I have used above snippet, however still output is not as expected
Any ideas?

So the space at the top of the TextView is padding used for characters outside the English language such as accents. To remove this space you can either set the android:includeFontPadding attribute to false in your XML or you can do it programmatically with the function setIncludeFontPadding(false).
Look at the SDK documentation for TextView if this is still unclear.
EDITED ANSWER
If setting the android:includeFontPadding attribute does not accomplish what you're trying to do, the other solution is to override the onDraw(Canvas canvas) method of the TextView that you're using so that it eliminates the additional top padding that Android adds to every TextView. After writing my original answer, I noticed that for some reason TextView includes extra padding in addition to the font padding. Removing the font padding as well as this additional padding perfectly aligns the text to the top of the TextView. Look at the code snippet below.
public class TopAlignedTextView extends TextView {
// Default constructor when inflating from XML file
public TopAlignedTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
// Default constructor override
public TopAlignedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs);
setIncludeFontPadding(false); //remove the font padding
setGravity(getGravity() | Gravity.TOP); //make sure that the gravity is set to the top
}
/*This is where the magic happens*/
#Override
protected void onDraw(Canvas canvas){
TextPaint textPaint = getPaint();
textPaint.setColor(getCurrentTextColor());
textPaint.drawableState = getDrawableState();
canvas.save();
//converts 5dip into pixels
int additionalPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getContext().getResources().getDisplayMetrics());
//subtracts the additional padding from the top of the canvas that textview draws to in order to align it with the top.
canvas.translate(0, -additionalPadding);
if(getLayout() != null)
getLayout().draw(canvas);
canvas.restore();
}
}

In your .xml, write that:
android:gravity="center|top"
The first "center" align your line horizontally in the center and the next "top" align vertically up.

The actual font itself has extra whitespace on the top and bottom I believe.
Usually what I do is give the view negative margins. The issue is that this looks bad when devices come with different fonts.
android:layout_marginTop="-5dp"
android:layout_marginBottom="-5dp"
You will have to adjust based on the text size, bigger text sizes require more negative padding.

Possible, you do not need make your view based on TextView. Try to extend View and write your text in onDraw().
Look at here: https://stackoverflow.com/a/32081250/1263771

I think your snippet is right. I guess you are bothered by some pixels between top of the letter "T" and top edge of the TextView? This space is required to render some letters that are not present in English alphabet.
Check that links to get idea:
http://unicode.org/charts/PDF/U0400.pdf
http://en.wikipedia.org/wiki/Typeface

Im not sure if i understand what you are meaning, but the text inside the textview is going to be align to the top if you use gravity=top on the text-view.
If you put a backgrund color on you textview you might see better what is happening...
I guess you problem is that the textview is centered since you use fill_parent on both height and width on the parent LinearLayout. The textview seems to be the only child so try putting gravity=top on the LinearLayout...
Thats the only thing i could think of...

Related

Slanting or Sloping UI design in Android

I am trying to design an UI very similar to this. I have been able to design it almost similar to the image above, but am not getting a way to implement the slanting or sloping part.
1) Can any one give an example layout of how can I implement the slanting layout?
2) And how can I place the FAB right there over the slant portion?
Any help would be really appreciated.
You can create a custom view with Slant top using Canvas and then place it over your TextView, to achieve this look and feel.
Code snippet for slant top custom view:
public class SlantView extends View {
private Context mContext;
Paint paint ;
Path path;
public SlantView(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
mContext = ctx;
setWillNotDraw(false);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected void onDraw(Canvas canvas) {
int w = getWidth(), h = getHeight();
paint.setStrokeWidth(2);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setAntiAlias(true);
path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.moveTo(0,0);
path.lineTo(0,h);
path.lineTo(w,h);
path.close();
canvas.drawPath(path, paint);
}
}
Code snippet for how to use it with TextView
<com.pix.app.views.SlantView
android:layout_width="match_parent"
android:layout_height="30dp"
android:id="#+id/slant_view"
/>
<TextView/>
Other way to achieve Slant View is this:
<FrameLayout android:layout_height="match_parent"
android:layout_width="match_parent"
android:background="#color/colorAccent"
xmlns:android="http://schemas.android.com/apk/res/android" >
<ImageView
android:layout_width="match_parent"
android:layout_height="280dp"
android:src="#color/colorPrimary"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:rotation="-70"
android:layout_marginTop="100dp"
android:background="#color/colorAccent"></LinearLayout>
</FrameLayout>
This will give you following output:
1) Can any one give an example layout of how can I implement the slanting layout?
You cannot. Views are always rectangular. You may however make it look slanted, i.e. with background image.
2) And how can I place the FAB right there over the slant portion?
You cannot have slant. It's just bottom edge of the square bounding box. So you should be able to put it there w/o any problem.
Normally images are represented in rectangular form. You can use padding/margin to design UI according to your need. Obviously other than sloping part will be transparent image.
You need to have the root or at least the immediate parent layout as FrameLayout, then,
1) For the image part, you can have a normal ImageView to show the image. Below that you can have a LinearLayout (blank, with white background).
Now just tilt the blank LinearLayout to an angle probably 45 degrees to cut the background image in slant style. Here, just set the XML property of the blank LinearLayout,
android:rotation = "45"
2) For the FAB, just move it to the cutting point, gravity of the layout should be set to right according to your screenshot.
Hope this helps. Cheers!
You'll have to write a custom View to do it. There's no native control that can do this.
Canvas's provide support for clipping paths, so it's easy enough to clip the image provided you can draw the image. That being said, drawing large images in an OnDraw implementation is less than straightforward (memory management and correct scaling and caching behaviour isn't difficult, but it's less than trivial)
An easier way to do it would be to extend something like a FrameLayout or some kind of ViewGroup. If you override dispatchDraw, you can push a clip path onto the canvas, which will clip children. And then you can put other controls inside your custom control.
Something like this:
public class ClipFrameLayout extends FrameLayout {
.... constructors and stuff...
#Override
void dispatchDraw(Canvas canvas)
{
canvas.save();
canvas.clipPath(mCustomClipPath);
super.dispatchDraw(canvas);
canvas.restore();
}
}

how to change the width of combined custom view in Android?

I wanted to implement a custom view which is combined by a ImageView and a TextView like this.
A class called IconTextView is created by extending TextView, and the onDraw function was rewrittend by this.
#Override
protected void onDraw(Canvas canvas)
{
if (bitmap != null)
{
//something are done before
canvas.drawBitmap(bitmap, src, target, getPaint());
canvas.translate(target.right + 2, 0);
}
super.onDraw(canvas);
}
Then a IconTextView was added into the main layout like this.
<com.liujilong.carson.icontextview.IconTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
mobile:iconSrc="#drawable/ic_launcher"
android:text="myIconTextView"
android:textSize="30sp"
/>
The IconTextView shown in my phone like this.
Sorry that i can not post picture.
The problem is that the width of the View was still the width of the TextView, and I translated the canvas. So the text could not show totally.
How can I change the width of the IconTextView?
I don't think you should have to do anything as drastic as override onDraw.
You could just have a horizontal LinearLayout that has an ImageView and a TextView
Even simpler, you can set the left drawable of the TextView using the the attribute android:drawableLeft="#drawable/my_icon". If you have to change it in code, the corresponding method is TextView.setCompoundDrawablesWithIntrinsicBounds(int,int,int,int). The images would have to be set up as drawable resources for this to work.
Is there something special about your custom view that these ideas won't work?

Android material aligning font baseline to 4dp grid

The Android Material Design Specification says "Type aligns to a 4 dp baseline grid."(http://www.google.com/design/spec/layout/metrics-and-keylines.html#)
It also says in the example graphics that a row in a list should be 72dp high.
It also indicates different font styles and what size the font should be and what its leading should be (http://www.google.com/design/spec/style/typography.html#typography-standard-styles).
I am trying to apply all of these rules to my application to adjust the layout of a row in my listing however I do not know how to space out the text boxes in my row.
I have 3 lines of text to display in each of my row
Heading line one - size 14sp
Details line two - size 12sp
Details line three - size 12sp
How do I ensure that the baseline of the text in each of these text boxes align with the grid? I can not align the bottom of the text box as this is not the base line, the bottom of the text box is the baseline + descent isn't it?
I need to space out the 3 lines of text as evenly as possible but ensure their baselines align to the grid.
I don't believe I can simply use padding/margins as this will not ensure the baselines of each of the lines of text align with the grid.
Futhermore when I do all this calculations I need to ensure that the row height is 72dp (for an mdpi device with normal font size specified).
Finally how would i specify the leading? As from what I understand this is the space from the baseline of the text of the row above to the top of the highest text in the bottom row. Again i cant use padding/margin as this is not from the baseline.
Edit: The Material Specification for lists have a little more information on how the Tiles in the list should appear when you have 3 lines of text. http://www.google.co.uk/design/spec/components/lists.html#lists-keylines
But it still does not indicate how the 3 lines of text are actually placed vertically so that their baselines align on the 4dp grid.
Update
Based on my answer I made a small library. It is better tailored for real world use than this answer, so please feel free to check it out.
Instead of simply ensuring 4dp alignment, it allows to set leading on all lines and the first line separately, and the last line descent:
Previous reply, left for historical purposes (large screenshot removed)
OK, this is pretty late, but I figured out how to do it in the least possible hackish way.
This method will land an existing view on a baseline grid, given that its upper bound is already on the baseline grid. Input parameters are the step of the grid (4dp in pixels, probably read from resources) and desired leading of the line (e.g. 20dp in pixels).
Note that this won't get you the exact heights that you need, but it will make TextView's size deterministic (i.e. height multiple of step with baseline being on the grid), making it much easier to layout as you need with the use of integer margins.
private static void setLeading(TextView view, int step, int leading) {
// This is to make the behavior more deterministic: remove extra top/bottom padding
view.setIncludeFontPadding(false);
// Get font metrics and calculate required inter-line extra
Paint.FontMetricsInt metrics = view.getPaint().getFontMetricsInt();
final int extra = leading - metrics.descent + metrics.ascent;
view.setLineSpacing(extra, 1);
// Determine minimum required top extra so that the view lands on the grid
final int alignTopExtra = (step + metrics.ascent % step) % step;
// Determine minimum required bottom extra so that view bounds are aligned with the grid
final int alignBottomExtra = (step - metrics.descent % step) % step;
view.setPadding(view.getPaddingLeft(), view.getPaddingTop() + alignTopExtra, view.getPaddingRight(), view.getPaddingBottom() + alignBottomExtra);
}
You can use a helper view of a specific height to baseline the textviews to this height.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.baselinetest.BaseLineHelperView
android:id="#+id/v_baselinehelper"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="#FF0000" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#id/v_baselinehelper"
android:text="#string/hello_world" />
</RelativeLayout>
Use this helper view
public class BaseLineHelperView extends View {
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public BaseLineHelperView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public BaseLineHelperView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public BaseLineHelperView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BaseLineHelperView(Context context) {
super(context);
}
#Override
#ExportedProperty(category = "layout")
public int getBaseline() {
return getHeight();
}
}
You should refer to this class in order to align text in the TextView to 4dp baseline grid:
https://github.com/nickbutcher/plaid/blob/master/app/src/main/java/io/plaidapp/ui/widget/BaselineGridTextView.java
You can use android:lineSpacingExtra and android:lineSpacingMultiplier in your XML file for line spacing. To have proper layout of single row of ListView it may help:-
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="88dp"
android:background="#fff"
android:orientation="horizontal"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingLeft="16dp" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="45dp"
android:layout_height="match_parent"
android:background="#000"
android:padding="16dp" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingRight="72dp"
android:paddingLeft="16dp"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textColor="#000"
android:textSize="16sp" />
<TextView
android:id="#+id/textView2"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="asdjkfah asdfakj sdfjshd sdgf dfg " />
</LinearLayout>
</LinearLayout>

Android multiline TextView , check if text fits , or check is TextView is full

I have an Android application layout which contains a multiline TextView. When the screen is in portrait orientation, the TextView can display a different length of text to when running in landscape mode. Also when running on a small screen, the TextView can display a different length of text to when running on a larger screen.
Is there any way I can check if the text fits or will be truncated? Or is there any way I can check if the TextView if full?
The problem is the TextView can potentially contain a different number of lines, depending on whether it is landscape, portrait, small screen, large screen, etc.
Thank you for your advice,
Best regards,
James
These answers didn't work very well for me. Here's what I ended up doing
Paint measurePaint = new Paint(myTextView.getPaint());
float pWidth = measurePaint.measureText("This is my big long line of text. Will it fit in here?");
float labelWidth = myTextView.getWidth();
int maxLines = myTextView.getMaxLines();
while (labelWidth > 0 && pWidth/maxLines > labelWidth-20) {
float textSize = measurePaint.getTextSize();
measurePaint.setTextSize(textSize-1);
pWidth = measurePaint.measureText("This is my big long line of text. Will it fit in here?");
if (textSize < TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 7,
getContext().getResources().getDisplayMetrics())) break;
}
myTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, measurePaint.getTextSize());
I'm not saying this will work for every situation as I'm certainly cutting corners here, but the general idea is to measure the text with the textview's paint and keep shrinking it until it will fit inside the textview.
I have found a "cheeky" solution to the problem of measuring the height of the text in a MULTILINE TextView :-
//Calculate the height of the text in the MULTILINE TextView
int textHeight = textView.getLineCount() * textView.getLineHeight();
if (textHeight > textViewHeight) {
//Text is truncated because text height is taller than TextView height
} else {
//Text not truncated because text height not taller than TextView height
}
However this solution has some caveats :-
Firstly regarding getLineHeight() , markup within the text can cause individual lines to be taller or shorter than this height, and the layout may contain additional first- or last-line padding. See http://developer.android.com/reference/android/widget/TextView.html#getLineHeight()
Secondly , the application needs to calculate the actual height of the TextView in pixels , and (in the case of my layout) it might not be as simple as textView.getHeight() , and calculation may vary from one layout to another layout.
I would recommend avoiding LinearLayout because the actual pixel height of the TextView can vary depending on text content. I am using a RelativeLayout (see http://pastebin.com/KPzw5LYd).
Using this RelativeLayout, I can calculate my TextView height as follows :-
//Calculate the height of the TextView for layout "http://pastebin.com/KPzw5LYd"
int textViewHeight = layout1.getHeight() - button1.getHeight() - button2.getHeight();
Hope that helps,
Regards,
James
Basically, you need to calculate the size of your textview and the size of your text when the orientation mode changed. Try ViewTreeObserver.OnGlobalLayoutListener to do so.
Inside the change orientation method:
main_view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
main_view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
//Calculation goes here
int text_size = getTextSize(text_view.getText().toString());
int text_view_size = text_view.getLayoutParams().width;
//Compare your text_size and text_view_size and do whatever you want here.
}
});
Here is the code of calculate the text_size:
private int getTextSize(String your_text){
Paint p = new Paint();
//Calculate the text size in pixel
return p.measureText(your_text);
}
Hope this help.
For checking whether a multiline (or not) TextView is going to be truncated, check this post.
Or, have you looked into using a scrolling textview? (marquee).. where the text will scroll by (horizontally, animated) if it is too long for a given width?
Here is an example TextView in a layout file, that has some of these characteristics:
<TextView
android:id="#+id/sometextview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
android:scrollHorizontally="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:freezesText="true"
android:textColor="#808080"
android:textSize="14sp"
android:text="This is a long scrolling line of text.. (etc)"/>
This extension function in Kotlin worked for me, just make sure to call it when the view is already laied out, e.g. view.post() is a good place for me;
/**
* Make sure to call this method only when view layout is finished, e.g. view.post() is a good place to check this
*/
fun TextView.isTruncated() = (lineCount * lineHeight) > height
Usage
textView.post {
if(isTruncated()) {
// Do something
}
}
Just check textView.getLineCount(), if line count > 1 then your text is multiline

textview cutting off a letter in android

this is a screen shot from my android. the text is "asd". however the "d" is slightly cut off. here is the relevant view:
<TextView
android:id="#+id/stuff"
android:padding="2dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="#+id/other_stuff"
android:layout_marginTop="33dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textStyle="italic" />
any idea as to what is causing this ?
ended up with a hacky solution which is adding a white space after the last italicized character
Ok this is pretty strange but I changed from android:maxLines="1" to android:singleLine="true" and now the text is not getting cut off.
This is caused by Android setting the TextView clipping rectangle for standard text and you using italic text. Italic text leans to the right, outside of the text bounds. For some reason Android does not account for this.
To prevent this, you can force a TextView to draw outside of its bounds by giving it a text shadow. If you set the shadow color to transparent, the result is simply un-clipped text. Add these to your TextView:
android:shadowColor="#00FFFFFF"
android:shadowDx="48"
android:shadowDy="0"
android:shadowRadius="1"
Also, if this doesn't work, try setting android:clipChildren=false on the parent layout.
I fixed it with setting the width of TextView to fill_parent instead of wrap_content...
This is my solution:
Format textview and measure. After that set width of textview with 1 pixel add to the width measured.
TextView textView = new TextView(this);
textView.setText("Text blah blah");
textView.setTypeface(typeface, Typeface.BOLD_ITALIC)
textView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
textView.setLayoutParams(new LayoutParams(textView.getMeasuredWidth() + 1, LayoutParams.WRAP_CONTENT));
Working for me. Hope these help.
To narrow down the source of the error try setting android:letterSpacing="0" This was the reason for cut off letters in my case. Another hint for too much letter spacing is that the cut off part get bigger when the text has more letters in it.
You can use .
android:layout_width="fill_parent"
istead of
android:layout_width="wrap_content"
I suspect the issue would not occur if you were not using italic text. I'd test that out first, and if using non-italic text renders it correctly, then it looks like a rendering issue, that would need working around with extra padding in the TextView to allow space for the italic letters.
the problem here is, the italic property. You have to set a particular width for your textview.
You could always create custom TextView that will use for example this font (cause in fact this is a problem with italic type):
Typeface tf = Typeface.createFromAsset(context.getAssets(), "fonts/Roboto-LightItalic.ttf");
setTypeface(tf);
More details here.
I encountered the same problem but with EditText when used some fonts. My solution works also with TextView:
Use padding
As TextView using Canvas.clipRect(float, float, float, float) in onDraw method to crop - create custom Canvas class and override this method (leave it empty).
Next сreate custom TextView and override two methods onSizeChanged and onDraw.
In onSizeChanged create bitmap with the size of TextView and our custom Canvas.
In onDraw first draw in bitmap by passing our custom Canvas to method super.onDraw. After that draw bitmap to target сanvas.
More detailed in my answer to the similar question here
Use this code:
textview.setShadowLayer(25f, 0f, 0f, 0)
Simple solution If you dont want to show ... to the end of text and want to show the complete text irrespective of number of lines than just add padding bottom 5dp to the textview.

Categories

Resources