Android Staticlayout Font Size - android

Is there anyway to change the font size of a static layout? This what I have so far to display the
text in the correct place to begin with.
int question_text_width = game_question_bg.getWidth();
int question_text_xPos = 100;
int question_text_yPos = 100;
StaticLayout question_text = new StaticLayout(text, text_paint, question_text_width, Layout.Alignment.ALIGN_CENTER, 1.2f, 1.0f, false);
//Position Text
canvas.translate(question_text_xPos, question_text_yPos);
//As a form of setMaxLine
canvas.clipRect(new Rect(0, 0, game_question_bg.getWidth(), game_question_bg.getHeight()));
question_text.draw(canvas);
//Reset canvas back to normal
canvas.restore();

You can set the font size using the TextPaint you pass as an argument when the StaticLayout is created:
TextPaint text_paint = new TextPaint();
float fontSize = 25;
text_paint.setTextSize(fontSize);
StaticLayout question_text = new StaticLayout(text, text_paint, question_text_width, Layout.Alignment.ALIGN_CENTER, 1.2f, 1.0f, false);

Related

android show dot character in StaticLayout

I would like to show a dot character "\u2022"
in a StaticLayout inside a custom view,
how it can be done?
TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
CharSequence text = "test \\u2022 test";
int textWidth = paint.measureText(text, 0, text.length());
StaticLayout layout = new StaticLayout(text, paint, (int) textWidth, Layout.Alignment.ALIGN_CENTER, 1f, 0f, true);

How is StaticLayout used in Android?

I need to build my own custom TextView so I have been learning about StaticLayout to draw text on a canvas. This is preferable to using Canvas.drawText() directly, or so the documentation says. However, the documentation doesn't give any examples for how do it. There is only a vague reference to StaticLayout.Builder being the newer way to do it.
I found an example here but it seems a little dated.
I finally worked though how to do it so I am adding my explanation below.
StaticLayout (similar to DynamicLayout and BoringLayout) is used to layout and draw text on a canvas. It is commonly used for the following tasks:
Measuring how big multiline text would be after being laid out.
Drawing text on a bitmap image.
Making a custom view that handles its own text layout (as opposed to making a composite view with an embedded TextView). TextView itself uses a StaticLayout internally.
Measuring text size
Single line
If you only have a single line of text, you can measure it with Paint or TextPaint.
String text = "This is some text."
TextPaint myTextPaint = new TextPaint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density);
mTextPaint.setColor(0xFF000000);
float width = mTextPaint.measureText(text);
float height = -mTextPaint.ascent() + mTextPaint.descent();
Multiline
However, if there is line wrapping and you need the height, then it is better to use a StaticLayout. You provide the width and then you can get the height from the StaticLayout.
String text = "This is some text. This is some text. This is some text. This is some text. This is some text. This is some text.";
TextPaint myTextPaint = new TextPaint();
myTextPaint.setAntiAlias(true);
myTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density);
myTextPaint.setColor(0xFF000000);
int width = 200;
Layout.Alignment alignment = Layout.Alignment.ALIGN_NORMAL;
float spacingMultiplier = 1;
float spacingAddition = 0;
boolean includePadding = false;
StaticLayout myStaticLayout = new StaticLayout(text, myTextPaint, width, alignment, spacingMultiplier, spacingAddition, includePadding);
float height = myStaticLayout.getHeight();
New API
If you want to use the newer StaticLayout.Builder (available from API 23), you can get your layout like this:
StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0, text.length(), myTextPaint, width);
StaticLayout myStaticLayout = builder.build();
You can tack on addition settings using dot notation:
StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0, text.length(), myTextPaint, width)
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setLineSpacing(spacingAddition, spacingMultiplier)
.setIncludePad(includePadding)
.setMaxLines(5);
StaticLayout myStaticLayout = builder.build();
Writing text on an image
I may expand this more in the future, but for now see this post for an example of a method that uses StaticLayout and returns a bitmap.
Making a custom text handling View
Here is an example of a custom view using a StaticLayout. It behaves like a simple TextView. When the text is too long to fit on the screen, it automatically line wraps and increases its height.
Code
MyView.java
public class MyView extends View {
String mText = "This is some text.";
TextPaint mTextPaint;
StaticLayout mStaticLayout;
// use this constructor if creating MyView programmatically
public MyView(Context context) {
super(context);
initLabelView();
}
// this constructor is used when created from xml
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
initLabelView();
}
private void initLabelView() {
mTextPaint = new TextPaint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density);
mTextPaint.setColor(0xFF000000);
// default to a single line of text
int width = (int) mTextPaint.measureText(mText);
mStaticLayout = new StaticLayout(mText, mTextPaint, (int) width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0, false);
// New API alternate
//
// StaticLayout.Builder builder = StaticLayout.Builder.obtain(mText, 0, mText.length(), mTextPaint, width)
// .setAlignment(Layout.Alignment.ALIGN_NORMAL)
// .setLineSpacing(0, 1) // add, multiplier
// .setIncludePad(false);
// mStaticLayout = builder.build();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Tell the parent layout how big this view would like to be
// but still respect any requirements (measure specs) that are passed down.
// determine the width
int width;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthRequirement = MeasureSpec.getSize(widthMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
width = widthRequirement;
} else {
width = mStaticLayout.getWidth() + getPaddingLeft() + getPaddingRight();
if (widthMode == MeasureSpec.AT_MOST) {
if (width > widthRequirement) {
width = widthRequirement;
// too long for a single line so relayout as multiline
mStaticLayout = new StaticLayout(mText, mTextPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0, false);
}
}
}
// determine the height
int height;
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightRequirement = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode == MeasureSpec.EXACTLY) {
height = heightRequirement;
} else {
height = mStaticLayout.getHeight() + getPaddingTop() + getPaddingBottom();
if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(height, heightRequirement);
}
}
// Required call: set width and height
setMeasuredDimension(width, height);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// do as little as possible inside onDraw to improve performance
// draw the text on the canvas after adjusting for padding
canvas.save();
canvas.translate(getPaddingLeft(), getPaddingTop());
mStaticLayout.draw(canvas);
canvas.restore();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="#dimen/activity_vertical_margin"
tools:context="com.example.layoutpractice.MainActivity">
<com.example.layoutpractice.MyView
android:layout_centerHorizontal="true"
android:background="#color/colorAccent"
android:padding="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
Notes
This, this, and this were useful in learning how to make a custom text handling view.
See Creating a View Class if you would like to add custom attributes that can be set from code or xml.
Here is my explanation for drawing multiline text on canvas.
Declare Paint object. Use TextPaint which is an extension of Paint.
TextPaint textPaint;
Initialize Paint object. Set your own color, size etc.
textPaint = new TextPaint();
textPaint.setAntiAlias(true);
textPaint.setTextSize(16 * getResources().getDisplayMetrics().density);
textPaint.setColor(Color.YELLOW);
Add getTextHeight function
private float getTextHeight(String text, Paint paint) {
Rect rect = new Rect();
paint.getTextBounds(text, 0, text.length(), rect);
return rect.height();
}
in your onDraw function put following lines like this
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text = "This is a lengthy text. We have to render this properly. If layout mess users review will mess. Is that so ? ";
Rect bounds = canvas.getClipBounds();
StaticLayout sl = new StaticLayout(text, textPaint, bounds.width(),
Layout.Alignment.ALIGN_CENTER, 1, 1, true);
canvas.save();
//calculate X and Y coordinates - In this case we want to draw the text in the
//center of canvas so we calculate
//text height and number of lines to move Y coordinate to center.
float textHeight = getTextHeight(text, 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();
}
Courtesy goes to KOC's post

Drawing HTML with Paint

I'm trying to draw text on a Google Map Fragment by generating a bitmap and placing it as a marker. However the text needs to be stylized via HTML. I was wondering if this is possible.
public static Bitmap createPureTextIcon(String text){
Paint textPaint = new Paint();
//textPaint.setTypeface(TypefaceUtil.get(m))
textPaint.setTextSize(MAP_DISPLAY_TEXT_SIZE);
textPaint.setAntiAlias(true);
textPaint.setTextAlign(Paint.Align.LEFT);
float baseline = -textPaint.ascent(); // ascent() is negative
int width = (int) (textPaint.measureText(text) + 0.5f); // round
int height = (int) (baseline + textPaint.descent() + 0.5f);
CharSequence m = ViewUtil.noTrailingwhiteLines(Html.fromHtml(text));
//height * 2
Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(image);
canvas.drawText(m.toString(), 0, baseline, textPaint);
//canvas.translate(0, height*4);
return image;
}
Example HTML
I mainly need it to render the <br />
<p style=\"text-align: center;\">Looking<br />at Earth </p>
Use a StaticLayout
StaticLayout layout = new StaticLayout(Html.fromHtml(m.toString()), textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
layout.draw(canvas);
Try canvas.drawText(HTML.fromHtml(text), 0, baseline, textPaint); fromHTML converts a string of HTML into a Spannable that can be drawn.

Set leading of paint / fontmetrics in android

I'm using a custom font and adding text to a canvas with a Paint object in android, e.g:
Paint paint = new Paint();
paint.setARGB(255, 255, 255, 255);
paint.setTextSize(29);
paint.setAntiAlias(true);
paint.setTypeface(font);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); // Text Overlapping Pattern
final TextPaint tp = new TextPaint(paint);
final TextPaint tpStroke = new TextPaint(strokePaint);
// canvas.translate(15, 5);
canvas.translate(20, 5);
StaticLayout sl = new StaticLayout(textOne.getText().toString(), tp, (finalBitmapOne.getWidth()-10), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
sl.draw(canvas);
I'm not happy with the leading / line height, how can I adjust it?
I can get the leading via:
FontMetrics fmPaint = paint.getFontMetrics();
fmPaint.leading;
But how can I adjust it?
From the documentation for getFontMetrics (emphasis mine):
Return the font's recommended interline spacing, given the Paint's settings for typeface, textSize, etc. If metrics is not null, return the fontmetric values in it.
You don't set this to change spacing, you read this to determine what the recommended spacing should be. You'll have to reposition coordinates manually yourself, i.e. translating the canvas again between drawing each line.

How to reliably determine the width of a multi line string?

I am trying to calculate the width of a multiline text paragraph. To my knowledge, the only class that can do this in Android is the StaticLayout (or DynamicLayout) class. When using this class i do no get the proper length of my text snippet but rather the measured the dimensions are sometimes smaller and sometimes greater depending on the text size.
So i am basically looking for a way to reliably measure the width of a multiline text string.
The following image shows how the measured width diverges from the actual text length in various text sizes.
The screenshot is created running the the following code in a custom View:
#Override
protected void onDraw( Canvas canvas ) {
for( int i = 0; i < 15; i++ ) {
int startSize = 10;
int curSize = i + startSize;
paint.setTextSize( curSize );
String text = i + startSize + " - " + TEXT_SNIPPET;
layout = new StaticLayout( text,
paint,
Integer.MAX_VALUE,
Alignment.ALIGN_NORMAL,
1.0f,
0.0f,
true );
float top = STEP_DISTANCE * i;
float measuredWidth = layout.getLineMax( 0 );
canvas.drawRect( 0, top, measuredWidth, top + curSize, bgPaint );
canvas.drawText( text, 0, STEP_DISTANCE * i + curSize, paint );
}
}
You could try using get text bounds.
private int calculateWidthFromFontSize(String testString, int currentSize)
{
Rect bounds = new Rect();
Paint paint = new Paint();
paint.setTextSize(currentSize);
paint.getTextBounds(testString, 0, testString.length(), bounds);
return (int) Math.ceil( bounds.width());
}
private int calculateHeightFromFontSize(String testString, int currentSize)
{
Rect bounds = new Rect();
Paint paint = new Paint();
paint.setTextSize(currentSize);
paint.getTextBounds(testString, 0, testString.length(), bounds);
return (int) Math.ceil( bounds.height());
}
I had a similar issue where the measured text width was off by a bit, once you set a Typeface to the paint this will go away.
So before you use the paint object in the StaticLayout just set the Typeface:
textPaint.setTypeface(Typeface.create("sans-serif", Typeface.NORMAL))
or whatever typeface you want.
Here's a way where you can get the multiline width of any text length. usableButtonWidth can be a given text space. I used usableButtonWidth = width of the button - padding. But any default value can be used.
Using StaticLayout, you can get the height of the text for the usableButtonWidth. Then I just try to reduce the available width by decreasing it by 1px in a loop. If the height increases then we come to know that the previously used width was the maximum permissible.
Return this value as the width of multiline text.
private int getTextWidth(){
if(this.getText().length() != 0){
Paint textPaint = new Paint();
Rect bounds = new Rect();
textPaint.setTextSize(this.getTextSize());
if(mTypeface != null){
textPaint.setTypeface(mTypeface);
}
String buttonText = this.getText().toString();
textPaint.getTextBounds(buttonText, 0, buttonText.length(), bounds);
if(bounds.width() > usableButtonWidth){
TextPaint longTextPaint = new TextPaint();
longTextPaint.setTextSize(this.getTextSize());
if(mTypeface != null){
longTextPaint.setTypeface(mTypeface);
}
StaticLayout staticLayout = new StaticLayout(this.getText(), longTextPaint, usableButtonWidth,
Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
int textLineCount = (int)Math.ceil(staticLayout.getHeight() / bounds.height());
int multiLineTextWidth = usableButtonWidth;
while ((int)Math.ceil(staticLayout.getHeight() / bounds.height()) == textLineCount && multiLineTextWidth > 1){
multiLineTextWidth--;
staticLayout = new StaticLayout(this.getText(), longTextPaint, multiLineTextWidth,
Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
}
return multiLineTextWidth+1;
} else {
return bounds.width();
}
} else {
return 0;
}
}

Categories

Resources