一般狀況下,在屏幕的特定位置上顯示文字是個很簡單的事情。使用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)。