How Is Staticlayout Used in Android

How is StaticLayout used in Android?

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.

enter image description here

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.

What is the substitute for deprecated StaticLayout

Use StaticLayout.Builder.
Go through here for more details: https://developer.android.com/reference/android/text/StaticLayout.Builder

for your case use:

StaticLayout.Builder sb = StaticLayout.Builder.obtain(text, 0, text.length(), paint, width)
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad (false);
StaticLayout layout = sb.build();

Android - Draw Text At The Bottom Of A Bitmap Using Static Layout

This turned out to be the correct answer. Subtract the height of the StaticLayout from the height of the canvas.

    val paint = TextPaint(Paint.ANTI_ALIAS_FLAG)
paint.color = Color.WHITE
paint.textSize = 40f //* context.resources.displayMetrics.density
paint.typeface = Typeface.DEFAULT_BOLD
//paint.textAlign = Paint.Align.
paint.setShadowLayer(1f, 0f, 1f, Color.DKGRAY)

val teststr = "Hello World"

val canvas = Canvas(bitmap)

val textLayout = StaticLayout(teststr, paint, canvas.width ,Layout.Alignment.ALIGN_CENTER, 1f, 0f, false)
canvas.save()
canvas.translate(0f,canvas.height - textLayout.height - 0.0f)
textLayout.draw(canvas)
canvas.restore()

StaticLayout positioning center of canvas not working like canvas.drawText?

I figured out the issue guys. It was because i was ignoring the width of the StaticLayout text's width while placing it to the centre.

Here I'm giving the StaticLayout the entire width of the View.

    staticLayout = StaticLayout(
"Hello world is helloworld",
ststicTextPaint,
w,
Layout.Alignment.ALIGN_CENTER,
1f,
0f,
false
)

While drawing the view

staticLayout.draw(canvas, ((width / 2)-staticLayout.width/2).toFloat(), (height / 2).toFloat())

I substracted the half of the width of the `StaticLayout width so that it'll place in the exact centre.
enter image description here

Draw StaticLayout text including HTML code in an Android canvas

StaticLayout.Builder.obtain.obtain(caption, 0, caption.length(), mTextPaint, bitmap.getWidth()).build() ;

In the doc, the first parameter is "source CharSequence: The text to be laid out, optionally with spans This value must never be null. " ( https://developer.android.com/reference/kotlin/android/text/StaticLayout.Builder#obtain)

So don't pass it a string of the html code, but a charsequence with spans that represent the interpreted html. For this purpose one should use Html.fromHtml ( https://developer.android.com/reference/android/text/Html.html#fromHtml(java.lang.String,%20int)).

Note: this answer is incomplete: HTML like <b style="color: rgb(255, 0, 0);">Rrr</b> will be interpreted without color, even if the flag Html.FROM_HTML_OPTION_USE_CSS_COLORS is used.



Related Topics



Leave a reply



Submit