How to draw a line to perfectly align top of a text? - android

I am able to align the baseline perfectly. The getLineBounds will give a baseline that is perfectly aligned with text (lineBottom - descent).
I am using rect.top as the topline which will give a line with padding on top see the screenshot.
The main issue is I have different fonts.And this code works perfectly on some fonts.
This is the code
int baseline = getLineBounds(i, rect);
int topLine = rect.top;
canvas.drawLine(rect.left - padding, baseline, rect.right + padding,
baseline, paint);
canvas.drawLine(rect.left - padding, topLine, rect.right + padding, topLine,
paint);
canvas.drawLine(rect.left - padding, (baseline + topLine) / 2, rect.right
+ padding, (baseline + topLine) / 2, paint1);
This is what I have tried.
1) Used a "StaticLayout" to get the top line but didn't make any difference.
2) Used paint to get the height of font and add it with baseline
paint.setTextSize(getTextSize());
paint.setTypeface(getCurrentTypFace());
paint.getTextBounds(getText().toString(), 0, getText().length(), r);
int height = r.height();
int topLine = baseline + height;
3) Tried with FontPadding =false android:includeFontPadding="false"
So my question is how to get the the topline like the baseline.
Any help with this is greatly appreciated.

Try paint.getTextBounds(...) again, but use r.top, (or actually -r.top) instead of r.height:
paint.setTextSize(getTextSize());
paint.setTypeface(getCurrentTypFace());
paint.getTextBounds(getText().toString(), 0, getText().length(), r);
int ascent = r.top;
int topLine = baseline + ascent;
r.height gives the distance from the very bottom of the character to the very top, whereas r.top should give the negative of the distance from the baseline to the very top.
If this doesn't work either, you might just need to draw the text to a temporary canvas in memory and run through the pixels row by row until you find a black one to measure the ascent yourself.
In either case, make sure you use a text that has capital letters in it. If you use a text with only lower case, it will likely give you the coordinates of the orange dashed line instead.
Note that both methods will give you the coordinate of a pixel at the very top edge of the font, not in the center of the stroke. Also, some letters in some fonts might overshoot the coordinate you really want to draw by quite a bit. Your safest bet is probably to pick a standard-string for measuring, like a capital O for the blue line and a lower-case o for the orange line (if it's not a script-font that has a little squiggle in the top right corner of the O that goes above the top of the circle...) and then store some small offset for each font that tells you how thick the line-width of the stroke is, so your lines go through the top of the letter rather than above it.

Related

Custom shaped textview, Android

I'm looking for a way of making custom shaped text input control on Android, like showed on screenshot.
What is need is to have some predefined places(gray rects) that will not be covered by text. So when user typing - text will lays out with some offsets from left side, or right side, depending on place of gray rect.
It is easy to do on iOS, just a couple lines of code and that is all. But I can not find a way of doing this on Android. Note that gray rects may not be a part of text input component. On iOS I simply put two UIImageView over the UITextView, and set up rects for excluding from rendering text:
CGFloat margin = 8;
CGRect firstPathRect = CGRectMake(0, 80, 160 + margin, 90 + margin);
CGRect secondPathRect = CGRectMake([UIScreen mainScreen].bounds.size.width - 160 - 2 * margin, 280, 160, 90 + margin);
UIBezierPath *path1 = [UIBezierPath bezierPathWithRect:firstPathRect];
UIBezierPath *path2 = [UIBezierPath bezierPathWithRect:secondPathRect];
self.textView.textContainer.exclusionPaths = #[path1, path2];
I hope somebody can help me with this task. Thanks in advance!
Check out this library for flowing text:
https://github.com/deano2390/FlowTextView

Centering Text on Android Canvas Including Accurate Bounds

Here is the situation. I am rendering to a canvas. Nothing else is being rendered, the canvas is effectively fullscreen. There is only one view.
I want to make a simple (text) button. This consists of two parts: visually drawing it, and checking bounds to see if it is pressed. These two things should be consistent.
Ideally the centering is optional, though I would expect switching it to be simple.
After far too many hours, I suspect it is not simply an alignment issue, but also a use of the API that is the source of the problem.
How can this be done?
Just use static layouts and then you can keep the text center aligned inside the canvas even if the size is increased by some limit by auto aligning the text.
Rect bounds = new Rect(x1, y1, x2, y2);// set the bounds
String textOnCanvas = "text to be wriiten";
StaticLayout sl = new StaticLayout(textOnCanvas, textPaint,
bounds.width(), Layout.Alignment.ALIGN_CENTER, 1, 1, true);
canvas.save();
float textHeight = getTextHeight(textOnCanvas, textPaint);
int numberOfTextLines = sl.getLineCount();
float textYCoordinate = bounds.exactCenterY() -
((numberOfTextLines * textHeight) / 2);
//text will be drawn from left
float textXCoordinate = bounds.left;
canvas.translate(textXCoordinate, textYCoordinate);
//draws static layout on canvas
sl.draw(canvas);
canvas.restore();
The parameter Layout.Alignment.ALIGN_CENTER of static layout will take care of the center alignment of the text in canvas.

How can I really center my text horizontally?

I have a Piano app with some round labels on it with the note names.
These note names need to be completely centered on the label.
I finally figured out how to do that vertically using ascent and descent. However, I am still not getting the horizontal alignment perfectly ok.
It is mainly about the rendering of single characters. Whatever I do, some of the characters are off. It's slightly, but it's there.
Notice how the E is slightly off to the right for example, and the B a bit to the left.
I am rendering the labels in onDraw(), not with custom views. I also tried with a TextView the size of the labels and using Gravity.CENTER, but this gave the same results. Also note that I tried Align.Center too.
Code:
usedPaint.setTextAlign(Align.LEFT); //Also tried it with Center
Rect textBounds = new Rect();
usedPaint.getTextBounds(infoText, 0, infoText.length(), textBounds);
float textWidth = usedPaint.measureText(infoText);
canvas.drawText(infoText, circleX - (0.5F * textWidth), circleY - ((usedPaint.descent() + usedPaint.ascent())/2),
usedPaint);
canvas.drawRect((circleX - (0.5F* textWidth)), circleY - (0.5F * textBounds.height()), circleX + (0.5F * textWidth), circleY + (0.5F * textBounds.height()), otherPaint);
The drawn box's width is the result of measureText. At the E character you see that somehow it measures some whitespace on the left, making the character drift off to the right.
This is using the standard font on Android 4.0.3. Using a custom TTF font results in the same kind of issues, but different for each character.
I am wondering what else I can do? (Besides getting over it ;) )
with center align just change canvas.drawText(infoText,
circleX - (0.5F * textWidth), ... to canvas.drawText(infoText,
circleX, ...

Android: Measure Text Height on a Canvas

I am currently working on rendering a Bitmap, that I then want to send to a mobile printer. However, I am struggling with measuring the height of my text, so I can advance the y position appropriately.
My basic bitmap/canvas/paint configuration is this (Font Size is 16 and the dimensions of the bitmap are 200x400 (width x height):
public MyRenderer() {
// Initialize bitmap
bitmap = Bitmap.createBitmap(200, 400, Bitmap.Config.ARGB_8888);
// Initialize canvas
canvas = new Canvas(bitmap);
// Initialize brush (Paint instance)
brush = new Paint();
brush.setTextSize(16);
brush.setTypeface(Typeface.SANS_SERIF);
brush.setColor(Color.BLACK);
brush.setStyle(Paint.Style.FILL);
brush.setAntiAlias(true);
brush.setTextAlign(Align.LEFT);
}
So far so good, now what I want to do is: If I use the Paint's method drawText I need to supply the x and y coordinates. As for x that's zero (assuming left aligned text) but as for y, I'd have to calculate the height of each text I print and add it up, so I can keep track of my current y position.
And this is where it gets odd: I am using the following method to determine the height of a text (using the Paint objected that I initialized previously - it's called "brush"):
public int measureHeight(String text) {
Rect result = new Rect();
// Measure the text rectangle to get the height
brush.getTextBounds(text, 0, text.length(), result);
return result.height();
}
The above method returns the following values for the following texts:
"Hello World" returns a height of 12
"A camera instance can be used to compute 3D transformations and generate a matrix." returns a height of 16
"Introducing Android Design: The place to learn about principles, building blocks, and patterns for creating world-class Android user interfaces. Whether you're a UI professional or a developer playing that role, these docs show you how to make good design decisions, big and small." returns a height of 16
It makes sense to me, that number 2 and 3 return a greater height than number 1 but if one line has a height of 12 (as number one does) - it makes no sense, that multiple lines have a height of 16 ?
Am I missing something here? There is a convenience method for measuring the width of a text (using an instance of paint and call measureText("myText") which works perfectly, however I am quite at a loss, when it comes to the height, as the above given results don't make any sense to me.
EDIT
I am aware, that getTextBounds probably does no auto-wrapping of multi-lined text, and that's ok, I already wrote a method for splitting text, but even if it just measures one line, the above given length values still seem unlikely.
I think it is because the "p" in "compute" extends below the baseline whereas "Hello World" only contains letters that are above the baseline.
Since the line distance should not depend on what specific letters your text happens to consist of you are probably looking for Paint.FontMetrics which can be obtained via Paint.getFontMetrics(). Compute descent - ascent + leading to get the recommended baseline distance (because ascent has a negative value).
There is a small error in the accepted answer. If you want the text height, you should use
Paint.FontMetrics fm = mTextPaint.getFontMetrics();
float textHeight = fm.descent - fm.ascent;
And if you want the line height, you should use
float lineHeight = fm.bottom - fm.top + fm.leading;
Leading is optional interline spacing, so if you need to get the line hight you can include it. But if you just want the text height, then you can leave it off.
Note
I've never actually seen leading be anything else than 0, and as far as I can tell it even seems to be ignored in the TextView source code (and its associated Layout, StaticLayout, etc.). Please correct me if I'm wrong. So it is probably safe it leave it out of line hight calulations, but I'm not completely sure about that.
See also
Getting text height from getTextBounds vs FontMetrics vs StaticLayout
Meaning of top, ascent, baseline, descent, bottom, and leading in Android's FontMetrics

Canvas clipping rect - right/bottom edge inclusive?

on Android, there's a Canvas class that represents a drawing surface. It has a clipping rect. Question - are the rect's right and bottom borders inclusive or exclusive? In other words - if the rect is (0, 0)-(10, 10), will the Canvas allow drawing in pixels at coordinates 10?
According to another StackOverflow question, right and bottom are exclusive, but top and left are inclusive.
As I say in my answer there (which I suppose is really a comment), this is consistent with other Java API, and has other benefits.
So, no, you won't be able to draw at ordinate 10. But it does mean that your Rect is a 10×10 pixel square.
Also, calculations are simpler, like:
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
Just for example, I know we have .getWidth() and .getHeight() methods.

Categories

Resources