自定义 View 中的文字测量和绘制

《自定义 View 中的文字测量和绘制》 jordan-whitt

之前做 LeafLoadingView 的时候,进度到达 100 后,需要将风扇替换为文字。这里的文字当时还是让我头疼了一会的,不过最终用缩放解决了问题。这让我不禁想,安卓 sdk 中肯定有绘制 text 的 api 我不知道,否则绘制文字的时候难点太多了。如果要我自己实现一个简易版的 TextView 根本不可能。

机缘巧合的某天,我在 github 上看一个项目,正巧看到了他们实现一个自定义 View 时测量文本用的方法,看起来挺好用的,google 了一下,今天学习了。

测量单行文本

这个技能相信大部分有过自定义 View 经历的人都学习过了,如果你有一个单行文本需要测量,那么通常你可以使用 TextPaint 或者 Paint 来测量。

例如:

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();

同样的,由于是单行文本,我们也不需要做换行操作,因此只要直接利用 drawText 方法绘制上去就可以了。这样的需求十分简单,相信即使没有接触过自定义 TextView, 也可以很快完成。

如果你是在界面代码中需要测量文本长度的话,那么下面的代码或许可以帮到你:

//在界面代码中测量文本
// 1. 对于显示在已知 TextView 上的文本
Paint paint = textView.getPaint();
// 2. 对于普通的文本
Paint paint = new Paint();
//... 设置各种显示的属性
// 获取到 Paint 对象之后,剩下的测量方式,请参考上面的代码

测量多行文本 – StaticLayout

我们知道利用 drawText 方法绘制文字,是不会自动换行的。如果文本超出容器的限制,多余的部分在屏幕上是不会显示的。因此,如果要利用 drawText 绘制长文本,需要计算长度进行文本截取和换行绘制的操作。这个过程稍微想象一下就让人怠惰起来一点也没有干劲。

这里就该提到我之前说到的新学习的类了—— StaticLayout。StaticLayout 封装了许多有用的方法,同时它也支持自动换行。事实上,TextView 中也使用到了 StaticLayout。

StaticLayout 的构造器中需要传入许多参数,刚刚见到的时候可以说是一头雾水。下面让我们来介绍一下各个参数的含义:

StaticLayout(
    CharSequence source,            //1.需要分行的字符串
    int bufstart,                   //2.需要分行的字符串从第几的位置开始
    int bufend,                     //3.需要分行的字符串到哪里结束
    TextPaint paint,                //4.画笔对象
    int outerwidth,                 //5.layout的宽度,字符串超出宽度时自动换行
    Alignment align,                //6.layout的对其方式,有ALIGN_CENTER, ALIGN_NORMAL, ALIGN_OPPOSITE 三种
    float spacingmult,              //7.相对行间距,相对字体大小,1.5f表示行间距为1.5倍的字体高度
    float spacingadd,               //8.在基础行距上添加多少
    boolean includepad,             //9.是否包含文字上下的空余部分,在某些语言下,切割这些空余部分会导致显示不全,默认为true
    TextUtils.TruncateAt ellipsize, //10.从什么位置开始省略 TextUtils.TruncateAt
    int ellipsizedWidth,            //11.超过多少开始省略,这个值仅用于省略,不影响 Layout 的宽度
    int maxLine                     //12.最大行数
)

上面的参数如果觉得不够清楚,可以查看源码。在 api 23 之后,加入了 Builder 的链式构造,每个构造方法中都有参数的说明,查看十分方便。

构造好之后,就可以直接利用 getHeight 方法计算出 Layout 的高度了。

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(); 

以上。

感谢:
1.stackoverflow – How is StaticLayout used in Android?
2.Android – drawing multiline text on bitmap
3.Usability of BoringLayout

    原文作者:Arnold_J
    原文地址: https://www.jianshu.com/p/13132312285f
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞