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()?
Related
I know how to stroke text using custom views(EditText or TextView) but I couldn't able to achieve something beautiful like this one, which is done using Photoshop. And yes, it has outer shadow too.
What I have done so far is adjusting stroke width and stroke join style. However, if I increase the stroke width, the stroke took place the whole text.
As far as I have searched, there is a library called MagicTextView but it also couldn't give the result like that above.
Update: I have tweaked things based on suggestion by #pskink. It works now. However I can't drag anymore. If I drag that EditText, there is some weird lines showed up like this.
Here is the code:
#Override public void onDraw(Canvas canvas) {
final int x = this.getLeft();
final int y = this.getBottom();
mText = this.getText().toString();
p.setStrokeWidth(30);
p.setStyle(Style.STROKE);
p.setStrokeJoin(Join.ROUND);
p.setColor(0xffffffff);
canvas.drawText(mText, x, y, p);
p.setStyle(Style.FILL);
p.setColor(0xff000000);
canvas.drawText(mText, x, y, p);
}
After a few hours of tweaking, I have fixed the weird line issue stated in updated question. I think I should post here as the answer.
#Override public void onDraw(Canvas canvas) {
mText = this.getText().toString();
p.setStyle(Style.STROKE);
p.setStrokeJoin(Join.ROUND);
p.setShadowLayer(10, 1, 1, 0xcfcccccc);
canvas.drawText(mText, 0, getLineHeight(), p);
p.clearShadowLayer();
p.setStrokeWidth(35);
p.setStrokeJoin(Join.ROUND);
p.setStyle(Style.STROKE);
p.setColor(0xffffffff);
canvas.drawText(mText, 0, getLineHeight(), p);
p.setStyle(Style.FILL);
p.setColor(0xff000000);
canvas.drawText(mText, 0, getLineHeight(), p);
canvas.translate(xPos, yPos);
}
xPos and yPos are x and y values from ACTION_MOVE onTouch event. And We need to add the line height as a Y for the canvas text.
However, if I increase the stroke width, the stroke took place the whole text.
If the problem is that the stroke is centered, and you want it outside the text, see: Android Paint stroke width positioning
You must precalculate the desired width and height according to your stroke.
I would suggest trying a very bold font. In the image below, the white stroke would be centered on the red line (which would be the outline of each black letter).
In response to your update:
If I drag that EditText, there is some weird lines showed up.
The line might be the result of focus on the EditText. You can remove the focus explicitly: Android: Force EditText to remove focus?
Alternatively, you can style the focus (perhaps to be invisible, if it doesn't adversely affect the rest of your UI): How to change the color of a focused EditText when using "android:Theme.Holo.Light"?
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.
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.
I'm currently using the following method to draw some text to the SurfaceView:
canvas.drawText("someText", 0, 0, paint);
Yet what if the text exceeds the width of the screen? Is it possible to define a region wherein the text can be drawn?
So now when the string's width exceeds the rectangles width, the text will be formatted to fit underneath the above text and so on eg.
"sometext"
"text carried"
"on"
Based on this answer, I believe this is what the Layout subclasses are used for. Per documentation:
A base class that manages text layout in visual elements on the
screen.
For text that will be edited, use a DynamicLayout, which will be
updated as the text changes. For text that will not change, use a
StaticLayout.
And with each subclass there's a note:
This is used by widgets to control text layout. You should not need to use this class directly unless you are implementing your own widget or custom display object, or would be tempted to call Canvas.drawText() directly.
Which sounds exactly like what you're doing.
It's basically a replacement for canvas.drawText(). It can be used in this fashion:
TextPaint tPaint = new TextPaint(paint);
StaticLayout sLayout = new StaticLayout(sText, tPaint, mWidth, widthToFill, Layout.Alignment.ALIGN_CENTER, 1.2f, 1.0f, false);
canvas.save();
canvas.translate(posX, posY);
sLayout.draw(canvas);
canvas.restore();
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);