在 Android 中如何使用 StaticLayout? [英] How is StaticLayout used in Android?

查看:27
本文介绍了在 Android 中如何使用 StaticLayout?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要构建自己的自定义 TextView 所以我一直在学习 StaticLayout 在画布上绘制文本.这比直接使用 Canvas.drawText() 或者

代码

MyView.java

public class MyView extends View {String mText = "这是一段文字.";TextPaint mTextPaint;静态布局 mStaticLayout;//如果以编程方式创建 MyView,则使用此构造函数公共 MyView(上下文上下文){超级(上下文);initLabelView();}//当从 xml 创建时使用此构造函数公共 MyView(上下文上下文,AttributeSet attrs){超级(上下文,属性);initLabelView();}私有无效initLabelView(){mTextPaint = new TextPaint();mTextPaint.setAntiAlias(true);mTextPaint.setTextSize(16 * getResources().getDisplayMetrics().密度);mTextPaint.setColor(0xFF000000);//默认为单行文本int width = (int) mTextPaint.measureText(mText);mStaticLayout = new StaticLayout(mText, mTextPaint, (int) width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0, false);//新的 API 替代////StaticLayout.Builder builder = StaticLayout.Builder.obtain(mText, 0, mText.length(), mTextPaint, width)//.setAlignment(Layout.Alignment.ALIGN_NORMAL)//.setLineSpacing(0, 1)//加,乘数//.setIncludePad(false);//mStaticLayout = builder.build();}@覆盖protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//告诉父布局这个视图有多大//但仍然尊重传递下来的任何要求(测量规范).//确定宽度整数宽度;int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthRequirement = MeasureSpec.getSize(widthMeasureSpec);如果(widthMode == MeasureSpec.EXACTLY){宽度 = 宽度要求;} 别的 {宽度 = mStaticLayout.getWidth() + getPaddingLeft() + getPaddingRight();如果(widthMode == MeasureSpec.AT_MOST){如果(宽度>宽度要求){宽度 = 宽度要求;//对于单行来说太长所以重新布局为多行mStaticLayout = new StaticLayout(mText, mTextPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0, false);}}}//确定高度整数高度;int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightRequirement = MeasureSpec.getSize(heightMeasureSpec);如果(heightMode == MeasureSpec.EXACTLY){高度 = 高度要求;} 别的 {高度 = mStaticLayout.getHeight() + getPaddingTop() + getPaddingBottom();如果(heightMode == MeasureSpec.AT_MOST){height = Math.min(height, heightRequirement);}}//需要调用:设置宽高setMeasuredDimension(宽度,高度);}@覆盖受保护的无效 onDraw(画布画布){super.onDraw(画布);//在 onDraw 中尽可能少做以提高性能//调整填充后在画布上绘制文本画布.save();canvas.translate(getPaddingLeft(), getPaddingTop());mStaticLayout.draw(画布);canvas.restore();}}

activity_main.xml

<com.example.layoutpractice.MyView机器人:layout_centerHorizo​​ntal="true"android:background="@color/colorAccent"机器人:填充=10dp"android:layout_width="wrap_content"android:layout_height="wrap_content"/></RelativeLayout>

注意事项

  • 这个this这个对于学习如何制作自定义文本处理视图很有用.

  • 请参阅创建视图类,如果您想添加可以从代码或 xml 设置的自定义属性.

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.

这篇关于在 Android 中如何使用 StaticLayout?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆