使用canvas與Paint在View中置中繪製文字,canvaspaint
本部落格只要沒有註明“轉”,那麼均為原創,轉貼請註明本部落格網站連結接
我們在自訂View中有的時候會想自己繪製文字,自己繪製文字的時候,我們通常希望把文字精確定位,文字置中(水平、垂直)是普遍的需求,所以這裡就以文字置中為例,看一下android中的文字應該如何繪製,它與Java又有什麼區別。
先來看看我們的目標,見
是我開啟了“顯示布局邊界”後截的圖,所有會有好多框框。
仔細觀察文字地區,我們會發現文字地區中有5條顏色不同的線。按著從上到下的順序,他們的名字分別是:
top:淺灰色
ascent:黃色
baseline:紅色
descent:藍色
bottom:綠色
這5條線到底是什嗎?android開發文檔中已經進行瞭解釋。
| top |
The maximum distance above the baseline for the tallest glyph in the font at a given text size. |
| ascent |
The recommended distance above the baseline for singled spaced text. |
| leading |
The recommended additional space to add between lines of text. |
| descent |
The recommended distance below the baseline for singled spaced text. |
| bottom |
The maximum distance below the baseline for the lowest glyph in the font at a given text size. |
我們先稍微跑一下題
如果你嘗試過將兩個TextView上下排列,沒有margin和padding,那麼你一定會發現,兩個TextView文字之間依然是有空隙的。首先我們需要設定includeFontPadding為false!但是依然有空隙,這時的空隙就是由top與ascent之間的空隙和bottom與descent直接的空隙造成的了。
那5條線的位置是由使用的字型和字型大小決定的。Paint提供了擷取上面5條線位置的方法。
一般情況下,我們使用的字元是在ascent與descent之間的,所以我們讓ascent與descent之間的部分相對我們的View置中即可。
以baseline為基準,向上為負,向下為正。ascent為負數,descent為正數。
Canvas中的drawText中的總座標是baseline,所以我們這裡要先算出baseline的位置才行。
baseline = (mHeight - (mFontMetricsInt.descent - mFontMetricsInt.ascent)) / 2 - mFontMetricsInt.ascent
使得ascent到View的是上邊距與descent到View下邊距距離一致即可,此段距離加上ascent的絕對值(-ascent)即為baseline的位置
private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStrokeWidth(3); mPaint.setTextSize(60); mPaint.setTextAlign(Paint.Align.CENTER); mPaint.setStyle(Paint.Style.STROKE); mFontMetricsInt = mPaint.getFontMetricsInt();}@Overridepublic void onDraw(Canvas canvas) { int x; if (mPaint.getTextAlign() == Paint.Align.LEFT) { //左 x = mWidth / 2 - (int) (mStringWidth / 2); } else if (mPaint.getTextAlign() == Paint.Align.CENTER) { //中 x = mWidth / 2; } else { //右 x = mWidth / 2 + (int) (mStringWidth / 2); } int xFrom = mWidth / 2 - (int) (mStringWidth / 2); int xTo = mWidth / 2 + (int) (mStringWidth / 2); // baseline = (mHeight - (mFontMetricsInt.descent - mFontMetricsInt.ascent)) / 2 - mFontMetricsInt.ascent // baseline = (mHeight - mFontMetricsInt.ascent - mFontMetricsInt.descent) / 2 int y = (mHeight - mFontMetricsInt.ascent - mFontMetricsInt.descent) / 2; Log.d(TAG, "ascent: " + mFontMetricsInt.ascent); Log.d(TAG, "descent: " + mFontMetricsInt.descent); Log.d(TAG, "top: " + mFontMetricsInt.top); Log.d(TAG, "bottom: " + mFontMetricsInt.bottom); Log.d(TAG, "baseline: " + y); // baseline mPaint.setColor(Color.RED); canvas.drawLine(xFrom, y, xTo, y, mPaint); // ascent mPaint.setColor(Color.YELLOW); canvas.drawLine(xFrom, y + mFontMetricsInt.ascent, xTo, y + mFontMetricsInt.ascent, mPaint); // descent mPaint.setColor(Color.BLUE); canvas.drawLine(xFrom, y + mFontMetricsInt.descent, xTo, y + mFontMetricsInt.descent, mPaint); // top mPaint.setColor(Color.LTGRAY); canvas.drawLine(xFrom, y + mFontMetricsInt.top, xTo, y + mFontMetricsInt.top, mPaint); // bottom mPaint.setColor(Color.GREEN); canvas.drawLine(xFrom, y + mFontMetricsInt.bottom, xTo, y + mFontMetricsInt.bottom, mPaint); mPaint.setColor(Color.BLACK); canvas.drawText(TEST_STRING, x, y, mPaint);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h;}private int mWidth;private int mHeight;private float mStringWidth;private float measureText() { mStringWidth = mPaint.measureText(TEST_STRING); return mStringWidth;}
注意:上面的那幾條線的位置和字型是有關的,比如,當你使用“方正姚體”的時候,會發現top和ascent重合了
private void init() { mTf = Typeface.createFromAsset(mContext.getAssets(), "fzyt.ttf"); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setTypeface(mTf); mPaint.setStrokeWidth(3); mPaint.setTextSize(60); mPaint.setTextAlign(Paint.Align.CENTER); mPaint.setStyle(Paint.Style.STROKE); // 畫空心矩形 mFontMetricsInt = mPaint.getFontMetricsInt(); Log.d(TAG, "measureText: " + measureText());}
垂直置中解決了,水平置中就容易了。因為……可以在Paint中直接設定。
mPaint.setTextAlign(Paint.Align.CENTER);
當然,這裡的對其方式只有左中右,即使這裡沒有設定置中,我們也是可以手動置中文字的。
int x;if (mPaint.getTextAlign() == Paint.Align.LEFT) { //左 x = mWidth / 2 - (int) (mStringWidth / 2);} else if (mPaint.getTextAlign() == Paint.Align.CENTER) { //中 x = mWidth / 2;} else { //右 x = mWidth / 2 + (int) (mStringWidth / 2);}
橫縱座標計算好了之後,我們就可以drawText了。
canvas.drawText(TEST_STRING, x, y, mPaint);
至此,問題全部解決,我們知道文字上面的那幾條線的位置,就能隨意放置我們的文字了。
繪製數位時候,1明顯比4瘦,但是我們可能會得到他們寬度相同的結果,也就沒有辦法“真正的置中”了。
附上layout檔案,如果你設定了padding,記得把padding也計算進去。
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#FFFFFF"> <com.example.TextTest.TextView android:layout_width="250dip" android:layout_height="100dip" android:layout_marginTop="10dip" android:layout_marginBottom="20dip" android:layout_marginLeft="10dip" android:layout_marginRight="20dip"/></LinearLayout>
最後我們來看看Java中的字型和Android的區別。
Java中字型的概念在這裡:Font Concepts。可以看到,這裡並沒有Android中的top和bottom的概念。
在維基百科中也有baseline相關解釋。這裡也是沒有提到Android中的top與bottom的概念
轉貼請保留以下連結
本人blog地址
http://su1216.iteye.com/
http://blog.csdn.net/su1216/
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。