一般状况下,在屏幕的特定位置上显示文字是个很简单的事情。使用TextView
,结合各类XxxLayout
,基本上想在哪显示文字均可以。但当显示的文字须要频繁更新的时候,使用TextView
可能就不是那么明智了。canvas
前段时间遇到这样一个需求,如图: 字体
TextView
来显示中间的数字,经过不断
setText
来更新文本显示。然而,运行起来后发现
TextView
的更新有很严重的卡顿,打开
TextView#onDraw
方法,发现这个方法里作了不少事情,
onDraw
如此频繁地被调用,卡顿是天然的。若是直接继承
View
,
onDraw
时使用
Canvas#drawText
实现文本绘制,省去
TextView
的大量额外计算,效率则会提高不少。
Canvas#drawText
的原型以下:动画
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint)
复制代码
绘制文本的时候,咱们须要传入(x,y)坐标参数让Canvas
知道咱们指望在哪一个位置绘制文本。那么问题来了,(x,y)究竟是哪一个点呢?x、y分别传入多少才能让文字在圈圈的中间显示呢?本文将经过这个例子,来说述Android中如何灵活地在想要的位置绘制文本。spa
上述需求中,若是咱们能找到文本的中心点和(x, y)的关系,而后把这个中心点和圈圈的中心点对齐,算出相应的(x, y),文本就能显示在圈圈的中心了。code
首先经过以下实例代码来观察文本位置和(x,y)坐标的关系:cdn
String text = "afp8";
canvas.drawText(text, x, y, paint);
// 画两条垂直相交的直线直观地展现点(x,y)的位置
drawHorizontalLine(canvas, y, Color.BLUE); // 自定义方法,画一条水平线
drawVerticalLine(canvas, x, Color.BLUE); // 自定义方法,画一条垂直线
复制代码
运行结果以下: blog
Paint
类提供了
Paint#descent
和
Paint#ascent
方法获取文本的降部和升部。如下实例代码结合降部和升部,画出两条水平线:
drawHorizontalLine(canvas, y + paint.descent(), Color.GREEN);
drawHorizontalLine(canvas, y + paint.ascent(), Color.RED); // paint.ascent()返回负数,所以是"+"
复制代码
运行结果以下: 继承
float textWidth = paint.measureText("afp8");
drawVerticalLine(canvas, x + textWidth / 2, Color.BLACK);
复制代码
运行结果以下: ip
上述实例中,要找到文本区域中心点的x坐标,实际上还有更简单的实现方式,就是设置画笔的对齐方式为Paint.Align.Center
。Paint#setTextAlign
做用是设置画笔绘制文本时(x,y)参考点的水平对齐方式,能够是Paint.Align.LEFT
或Paint.Align.CENTER
或Paint.Align.Right
(默认是LEFT),三者的区别可经过如下实例代码来体现:文档
drawHorizontalLine(canvas, y, Color.BLUE);
paint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("left", x, y, paint);
drawVerticalLine(canvas, x, Color.BLUE);
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("center", x + 550, y, paint);
drawVerticalLine(canvas, x + 550, Color.RED);
paint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText("right", x + 1200, y, paint);
drawVerticalLine(canvas, x + 1200, Color.GREEN);
复制代码
运行结果以下:
根据文档,Paint#getTextBounds
能够获取一个能包裹住文本的最小的矩形,矩形原点默认是(0,0)。参考如下实例代码,并将Paint#getTextBounds
和Paint#measureText
获取的文本宽度进行比较:
paint.getTextBounds(text, 0, text.length(), rect);
// 获取的矩形的原点是(0,0),加100和加300是为了让矩形的左上角和文本的左上角对齐
canvas.drawRect(rect.left + 100, rect.top + 300,
rect.right + 100, rect.bottom + 300, paint);
float textWidth = paint.measureText(text);
drawVerticalLine(canvas, x + textWidth, Color.GREEN);
复制代码
运行结果以下:
Paint#getTextBounds
获得的矩形是能包裹文本的最小的矩形,对齐后矩形的四边都紧贴着文本。而
Paint#measureText
获取的文本宽度实际上比
Paint#getTextBounds
获得的矩形宽度要大。并且标注文本区域的升部和降部的两条水平线间的距离比
Paint#getTextBounds
获得的矩形的高度也要大一些。
维基百科中说东亚字体无基线,也无升部和降部,那Android里中文的绘制是怎样的一种状况呢?先看一个中文字符绘制的实例:
canvas.drawText("中文", x, y, paint);
drawHorizontalLine(canvas, y + paint.descent(), Color.GREEN);
drawHorizontalLine(canvas, y + paint.ascent(), Color.BLACK);
复制代码
运行结果以下:
Canvas#drawText
进行文本绘制时,参考点(x,y)的x坐标根据画笔的对齐方式而定,能够经过Paint#setTextAlign
设置左、中、右对齐。而y坐标是基线的y坐标。Paint#ascent
和Paint#descent
获取文本区域的升部和降部,进而能够定位文本区域的上下边沿。Paint#getTextBounds
获取一个能包裹住文本的最小矩形,矩形原点默认为(0,0)。