我需要构建自己的自定义TextView,因此我一直在学习有关StaticLayout以便在 Canvas 上绘制文本的信息。这比直接使用Canvas.drawText()更好,否则documentation会这样说。但是,文档没有提供有关如何执行此操作的任何示例。仅有模糊的引用 StaticLayout.Builder 是更新的方法。

我找到了一个here示例,但似乎有些过时了。

我终于完成了工作,但是我在下面添加了解释。

最佳答案

StaticLayout(similar to DynamicLayout and BoringLayout )用于在 Canvas 上布局和绘制文本。通常用于以下任务:

  • 测量布局后多行文本的大小。
  • 在位图图像上绘制文本。
  • 制作一个可处理其自己的文本布局的自定义 View (与使用嵌入式TextView制作一个复合 View 相反)。 TextView本身使用StaticLayout internally

  • 测量文字大小

    单行

    如果只有一行文本,则可以使用PaintTextPaint对其进行度量。
    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();
    

    多行

    但是,如果有换行并且需要高度,那么最好使用StaticLayout。您提供宽度,然后可以从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();
    

    新API

    如果要使用较新的 StaticLayout.Builder (可从API 23获得),则可以这样获得布局:
    StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0, text.length(), myTextPaint, width);
    StaticLayout myStaticLayout = builder.build();
    

    您可以使用点表示法附加设置:
    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();
    

    在图像上书写文字

    将来我可能会对此进行更多扩展,但现在请参见this post,该示例使用StaticLayout并返回位图的方法的示例。

    制作自定义文本处理 View

    这是使用StaticLayout的自定义 View 的示例。它的行为就像一个简单的TextView。当文本太长而无法在屏幕上显示时,它将自动换行并增加其高度。

    android - Android中如何使用StaticLayout?-LMLPHP

    代码

    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>
    

    注释
  • Thisthisthis在学习如何制作自定义文本处理 View 时很有用。
  • 如果要添加可以从代码或xml设置的自定义属性,请参见Creating a View Class
  • 09-05 01:06