I am drawing formatted text on canvas using DynamicLayout, and I need to implement some sort of zooming. I tried to use canvas.scale(...) for it. But when text contains highlights, text is scaled inconsistent with background, like this:
Code which draws text is very simple:
canvas.scale(zoom, zoom);
TextPaint textPaint = new TextPaint();
textPaint.setAntiAlias(true);
DynamicLayout layout = new DynamicLayout(text, textPaint, width,
Layout.Alignment.ALIGN_NORMAL, 1.0f, 1.0f, false);
layout.draw(canvas);
How to make it right way?
I've solved this issue, setting textPaint.setSubpixelText(true);
Related
I have an AppWidget RemoteView with a ListView that holds one child - ImageView with custom font text.
Here is my code:
Typeface tf = Typeface.createFromAsset(mContext.getAssets(),"fonts/myfont.ttf");
TextPaint textPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
textPaint.setTypeface(tf);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setColor(mycolor);
textPaint.setTextSize(px);
StaticLayout staticLayout = new StaticLayout(textString, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
Bitmap bitmap = Bitmap.createBitmap(width, staticLayout.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
staticLayout.draw(canvas);
rv.setImageViewBitmap(R.id.imageViewText, bitmap);
It works correctly.
But, every time the text is long enough to exceed 50 / 60 lines, it shows as empty, but i can scroll the listview (so the staticLayout is not empty, because the bitmap's height is not 0).
Another important thing is the following behavior:
When i start drag the widget, the text appears. Immediately after i drop it, the text disappears again.
What can cause that kind of behavior?
Maybe someone faced similar problem can help.
EDIT:
I tried to save the bitmap as png to the cache directory, then set the image via setImageViewUri method, but the result is the same. Transparent image with the right dimensions, and when drag and drop - its visible.
I am trying to draw multiline text to a bitmap with the font Latto-Reg, and StaticLayout seems to have problems with it.
paint.setTextSize(label.fontSize);
paint.setTypeface(face);
StaticLayout textLayout = new StaticLayout(label.text, paint, (int)StaticLayout.getDesiredWidth(label.text, paint), Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
Bitmap bitmapAux = Bitmap.createBitmap(textLayout.getEllipsizedWidth(), textLayout.getHeight(), Bitmap.Config.ALPHA_8);
canvas.setBitmap(bitmapAux);
canvas.save();
canvas.translate(0, textLayout.height());
textLayout.draw(canvas);
canvas.restore();
The texture has padding on top and bottom depending on the font and size, while the text fits perfectly in the bitmap it is a lot of wasted memory space and makes laying it out to be off by a random amount.
I tested using single-line drawing and the bitmap was perfectly fitting the text
paint.getTextBounds(label.text, 0, label.text.length(), rect);
Bitmap bitmapAux = Bitmap.createBitmap(rect.width(), rect.height(), Bitmap.Config.ALPHA_8);
canvas.drawText(label.text, -rect.left, -rect.bottom, paint);
I have tried getting all kinds of metrics from StaticLayout and all of them seem to be off from the text: line 0 bounds, line 0 top, last line bottom...leading to the same padding problems.
EDIT:
I solved the problem by using offset-based single line drawing. Still the StaticLayout class was drawing incorrectly with several different non-standard fonts and I want to know why.
Looking at the android developper page, it looks like it's designed to handle both the multi-line case and being used next to another Layout well, and hence there is space on top of the line of text so that if you place it directly below another Layout it will be correctly spaced. In essence, it's just not designed for what you are trying to achieve.
Overall, it may be easier to get the Text bounds from Paint.getTextBounds() to know what the extent of the text will be within the Layout.
I've created a minimal working example of what I think you're trying to accomplish: creating a bitmap precisely large enough to contain the text rendered through a StaticLayout.
It seems that there are a few things wrong with your code:
You're needlessly translating vertically inside the bitmap;
There doesn't appear to be a height() method for StaticLayout.
Here's my result:
I added a green background to illustrate the size of the bitmap, but otherwise, my code differs very little from yours:
public void createTexture() {
int width = textLayout.getEllipsizedWidth();
int height = textLayout.getHeight();
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas2 = new Canvas(bitmap);
Paint p2 = new Paint();
p2.setStyle(Style.FILL);
p2.setColor(Color.GREEN);
canvas2.drawRect(0, 0, width, height, p2);
textLayout.draw(canvas2);
}
I created a very simple custom component to draw the bitmap:
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, paint);
}
It seems that perhaps you're translating to draw multiple textures after one another. I'd recommend that you do so in your draw method instead, translating vertically in the height of the previous texture after drawing it.
How do you horizontally center a String in a surface view?
I don't want
c.drawText(""+score, c.getWidth()/2, y);
Because this will display text starting at the center of the screen.
I want something like
c.drawText(""+score, c.getWidth()/2-score.length()/2*fontSize, y);
Is there an equation to solve this problem? If not, are there any other ways to center text in a surface view?
BTW I'm using a custom font, I don't know if that changes anything or not. If you need to know any extra information about the font please comment.
You don't have to calculate manually. You just need to specify some extra parameters in your Paint object:
Paint paint = new Paint();
paint.setColor(textColor);
paint.setTextSize(28);
paint.setTypeface(typeface);
paint.setTextAlign(Align.CENTER);
The key is that you need to specify Align.CENTER, in this way you can just call:
canvas.drawText("CENTERED TEXT", canvas.getWidth()/2, posY, paint);
I'm drawing some text onto a canvas, and am using a StaticLayout to wrap the text across the screen. I want to align the text so that the bottom of the text is on the bottom of the screen.
To do this I need to know how many lines the StaticLayout has wrapped the text into, so i can multiply that by the font size, and offset it that much from the height of my component.
This is my StaticLayout:
main = new TextPaint();
main.setTextSize(textSize);
main.setColor(Color.WHITE);
bottomText = new StaticLayout("Long text that requires wrapping.", main, getWidth(), Layout.Alignment.ALIGN_CENTER, 1f, 1.0f, false);
And I'm moving it down by translating my canvas:
canvas.translate(0, getHeight() / 2);
bottomText.draw(canvas);
canvas.restore();
So; how do I align it to the bottom, or get the number of lines it has been split into?
How about StaticLayout.getLineCount()?
I'm writing a custom View, but I can't really figure out how to use clipRect on a Canvas. I need this because I'm calling draw(Canvas) on another object and I'd like to give it my own (clipped) Canvas. My current solution is:
StaticLayout sl = new StaticLayout(text, tp, (int) (rect.right - rect.left), Alignment.ALIGN_NORMAL, 1f, 0f, true);
Bitmap layoutBitmap = Bitmap.createBitmap((int) (rect.right - rect.left), (int) (rect.bottom - rect.top), Config.ARGB_8888);
Canvas layoutCanvas = new Canvas(layoutBitmap);
sl.draw(layoutCanvas);
canvas.drawBitmap(layoutBitmap, null, rect, null);
However, this feels dirty, creating a new bitmap and a new canvas every time (I'm using this method to draw text in a box, see my previous question).
What I'd like to do is something like this:
StaticLayout sl = new StaticLayout(text, tp, (int) (rect.right - rect.left), Alignment.ALIGN_NORMAL, 1f, 0f, true);
canvas.save();
canvas.clipRect(rect, Region.Op.REPLACE);
sl.draw(canvas);
canvas.restore();
That "feels" much better, except that it doesn't work. Am I using clipRect wrong? Do I not understand what it's actually for or how to use it? Please advise.
P.S. My understanding of clipRect is that after that clipRect call, 0, 0 should actually translate to rect.left, rect.top.
After a bit of experimenting, it seems that clipRect only restricts drawing to the given rect, so that any draw calls outside that rect will be clipped to that rect. Thus, my understanding of clipRect was wrong.
This means that, in order to use StaticLayout I'd have to first draw it to a Bitmap that is the size of my rect and then draw that Bitmap to my Canvas at the coordinates I need.
However, I have resorted to using Canvas.drawText and TextPaint.breakText instead (so I don't have to create a Bitmap everytime).