I'm writing a custom View for a timer, but I can't get a properly fixed-width font for the numbers in the middle. Here's the relevant code (from two different methods):
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTypeface(Typeface.create(Typeface.MONOSPACE, Typeface.NORMAL));
// Get rectangle that is the size of the text
mTextPaint.getTextBounds(
getTimeText(),
0,
getTimeText().length(),
mTextBounds);
// X and Y coordinates of text
float mTextPosX = (width / 2) - (mTextBounds.width() / 2);
float mTextPosY = (height / 2) + (mTextBounds.height() / 2);
// Draw text
canvas.drawText(
getTimeText(),
mTextPosX,
mTextPosY,
mTextPaint);
This seems to work fine, but whenever one of the numbers changes to a 1, that 1 takes up less space and the whole chunk of text moves over a bit. This obviously should not be happening, since I'm using a monospace font. Can anyone shed some light on how I might fix this?
I had the same problem. A way to fix it is by using an external font in which "1" behaves as expected. A website with many fonts to try is this one: http://www.fontcubes.com/ . A related discussion on how to use external fonts in Android is here: How to change fontFamily of TextView in Android .
Related
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
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, ...
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
I am programming an app that shows a lot of verses/poems so text wrapping is not an option for me. I would like the text to be as big as possible (doesn't have to recalculate each time a new text is shown, should just allow the biggest text to fit on the screen) without extending screen size. It should not visually scale or take longer for the text to appear.
Is this possible?
Thanks
I would suggest a simple search for the best point size using the largest text that you need to fit. This can be done once at start-up. (Well, maybe twice—once for landscape and once for portrait). The first step would be to initialize a Paint with the typeface you want to use for display. Then call this function to
public void setBestTextSize(String longestText, int targetWidth, Paint paint) {
float size = paint.getTextSize(); // initial size
float w = paint.meaasureText(longestText);
size = targetWidth * size / w;
paint.setTextSize(size);
// test if we overshot
w = paint.measureText(longestText);
while (w > targetWidth) {
--size;
paint.setTextSize(size);
w = paint.measureText(longestText);
}
A binary search in the loop might be theoretically faster, but this should do pretty well since text width does scale approximately linearly with font size and the first step before the loop should get the size pretty close.
An alternative approach, which deals nicely with view size changes, is shown in this thread.
I have an app that contains a custom view, and in it I want to use a custom font. Unfortunately, this results in really ugly text being rendered.
My custom view extends Surface (and just realized: is this a bad idea), and I draw the text with the following code:
// p = new Paint();
Typeface font = Typeface.createFromAsset(parent.getAssets(), "komtit.ttf");
p.setColor(Color.BLACK);
p.setTypeface(font);
c.drawText(this.text, x + width / 2 - p.measureText(this.text) / 2, y + height / 2, p);
The result looks like this:
http://img.skitch.com/20101014-rxw8j8igj1jci2fx9ui32ejcp.jpg
Not fun. I'm using p.setFlags(Paint.ANTI_ALIAS_FLAG) but even with that the result is just fudgly.
So, is there a way to improve custom TTF rendering, or should I just stick to the system fonts?
Answering my own question here. The problem was that I was extending Surface (which I really in the end didn't have a proper reason for) which apparently uses OpenGL for rendering and OpenGL and custom TTF fonts don't mix.
When I switched to extending just View, the font looks perfect.