Draw Center Text(FontMetrics解析)

weixin_34120274發表於2016-05-07

本文首發在我的個人部落格ghui.me歡迎指教

使用canvas.drawText方法可以直接在canvas上畫text,通常情況下需要文字在水平與垂直方向上均居中顯示,水平居中很容易實現。假設我們需要在rectF所標識的區域中實現這樣的效果,
程式碼如下:

Paint.Align align = paint.getTextAlign();
float x;
float y;
//x
if (align == Paint.Align.LEFT) {
    x = rectF.centerX() - paint.measureText(text) / 2;
} else if (align == Paint.Align.CENTER) {
    x = rectF.centerX();
} else {
    x = rectF.centerX() + paint.measureText(text) / 2;
}

Paint物件可以設定繪製文字開始的錨點,如果alignLEFT則代表 canvas.drawText(String text,float x,float y,Paint paint)x,y為文字最左邊的點,
依次類推:若為CENTER則相當於直接設定文字在水平方向上的中點,為RIGHT則相當於設定文字最右邊的點。
總之,通過上面的程式碼可以完美實現文字在水平方向上居中顯示,下面看一下如何實現在垂直方向上居中顯示。
通常情況下會有下面這樣的程式碼:

int textSize = paint.getTextSize();
y = rectF.centerY() + textSize/2f;

上面的程式碼基本上可以實現垂直居中,但如果你仔細觀察會發現文字略微有點靠下,似乎是沒有居中。實際上文字應該算是居中了,但在視覺上它的確沒有居中,要想搞明白其中的原因,需要了解一下Paint.FontMetrics類,原始碼如下:

public static class FontMetrics {
/**
 * The maximum distance above the baseline for the tallest glyph in
 * the font at a given text size.
 */
public float   top;
/**
 * The recommended distance above the baseline for singled spaced text.
 */
public float   ascent;
/**
 * The recommended distance below the baseline for singled spaced text.
 */
public float   descent;
/**
 * The maximum distance below the baseline for the lowest glyph in
 * the font at a given text size.
 */
public float   bottom;
/**
 * The recommended additional space to add between lines of text.
 */
public float   leading;
}

通俗的講

  • top是一行文字的上邊界
  • ascent是文字可視區域的上邊界
  • descent是文字可視區域的下邊界
  • bottom是一行文字的下邊界
  • leading是行與行之間的間距(通常為0,bottom與descent及top與ascent之間的間距足夠間隔行行)

為了更好的理解上面的概念請看下面的圖:

632614-89c36d9cbfde84b0.png

現在回過頭來看一下為什麼上面我們垂直居中的程式碼有問題,從上面的圖中可以發現:文字的可視區域在ascent與descent之間,而我們上面的程式碼實際上是將topbottom之間的部分居中了,並沒有將可視區域居中,實際上通過log可以發現topascent之間的間距大概是bottomdescent之間間距的5倍,這也就解釋了為什麼上面的程式碼在垂直方向上
略微偏下了,原因找到了,解決方法也自然有了.

為了達到文字在視覺上的垂直居中效果,我們需要將文字的可視區域在垂直方面上的中點與rectF的centerY重合,
,就需要計算出可視區域在垂直方向上的中點與baseline之間的間距deltaY,使baseline的y值=rectF.centerY + deltaY。這樣就能保證文字的可視區域的中點位於rectF的中點了,達到了視覺上的垂直居中。整體程式碼如下:

public static void drawCenterText(String text, RectF rectF, Canvas canvas, Paint paint) {
    Paint.Align align = paint.getTextAlign();
    float x;
    float y;
    //x
    if (align == Paint.Align.LEFT) {
        x = rectF.centerX() - paint.measureText(text) / 2;
    } else if (align == Paint.Align.CENTER) {
        x = rectF.centerX();
    } else {
        x = rectF.centerX() + paint.measureText(text) / 2;
    }
    //y
    Paint.FontMetrics metrics = paint.getFontMetrics();
    float acent = Math.abs(metrics.ascent);
    float descent = Math.abs(metrics.descent);
    y = rectF.centerY() + (acent - descent) / 2f;
    canvas.drawText(text, x, y, paint);
}

抽成了一個方法,可以直接拿來用,這裡需要注意一點drawText垂直方向上是基於baseline畫的,baseline之上的ascenttop均為負值,之下的descentbottom為正值,baseline的y值為0;

個人微信公眾號已開通:CoderGhui ,歡迎關注!

632614-f445a89884c5aaf1.jpg

相關文章