前言:有經驗的Android開發者,應該都會遇到在自定義View的時候,在View的某個地方寫文字,那麼當你在自定義的View中寫文字的時候,能夠做到定點寫文字嗎?能夠指哪寫哪嗎?寫出來的文字的位置和自己想要的位置一樣嗎?即使你最後寫的文字的位置和自己想象的位置是一樣的,那麼你知道其中的原理嗎?如果其中有一個你不能回答出來,那就認真的閱讀本文吧!本文會給出你想要的答案...
一個小例子
看下下面的圖,假如下面的圖是我們要做出的效果
很簡單,有沒有?就是在一個圓的中心寫文字。紅色的圓形很好畫出來,那麼我們怎麼將文字寫在圓的中心點上呢?第一時間想到的是拿到圓中心點的座標,然後直接呼叫drawText()
方法來寫文字。實現的程式碼大致如下
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int measuredHeight = getMeasuredHeight();
int measuredWidth = getMeasuredWidth();
//cx,cy為圓的中心點的座標
int cx = measuredWidth / 2;
int cy = measuredHeight / 2;
canvas.drawCircle(cx, cy, mRadius, mPaint);
mPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.sp18));
mPaint.setColor(Color.WHITE);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setStrokeWidth(getResources().getDimensionPixelSize(R.dimen.dp1));
canvas.drawText("wizardev", cx, cy, mPaint);
}
複製程式碼
現在,看下上面程式碼實現的效果
上圖中的黃線是在圓的中心點畫的線,可以發現上面程式碼實現的效果,明顯不是我們想要的效果,為什麼會這樣呢?下文會給出答案。
drawText()中的基線
在Android中自定義View的時候,怎麼讓系統知道應該在哪裡畫出我們想要的圖形呢?比如畫上面的圓,這時我們就要告訴系統,我們要畫的圓形的圓心在什麼位置,告訴系統我們想要的圓的半徑是多少,然後系統就能在合適的位置畫出你想要的圓了。同樣,在畫文字的時候我們要指定文字在什麼位置,而指定的座標的位置就是文字的基線。
要理解drawText()
中的基線是什麼,需要先了解一下darwText()
方法,darwText()
方法有四個引數,如下
drawText(@NonNull String text, float x, float y, @NonNull Paint paint)
複製程式碼
第一個引數為你想要寫的內容,第二個引數為文字開始的X軸座標,第三個引數為文字開始的Y軸座標,第四個引數為畫筆。以第二個第三個引數畫的一條水平線,就是drawText()
的基線。如上文中的第二張圖,黃色的線即為drawText()
基線,
注:第二和第三個引數不一定為文字開始的座標,也可能為文字中心的座標或則文字結尾的座標,具體是哪種座標與Paint
中的setTextAlign()
方法有關。
可以得出結論,只要確定了基線的位置就確定了要畫的文字的位置了。那麼給定一個座標,怎麼確定基線的位置呢?其實畫文字的時候,除了基線以外,還有其他幾條線,其他幾條線的位置如下圖
這幾條線的意義分別是:
- ascent: 系統建議的,字元所佔的最高高度所線上
- descent:系統建議的,字元所佔的最低高度所線上
- top: 可繪製的最高高度所線上
- bottom: 可繪製的最低高度所線上
這幾條線的位置可以通過FontMetrics
物件獲得。
FontMetrics
(1)FontMetrics概述
描述給定文字的各種度量值的類,它有五個成員變數,分別為top
、ascent
、descent
、bottom
、leading
。這幾個成員變數的值都是相對基線位置的距離,如:
FontMetrics.top = top的Y座標 - 基線
(2)獲取FontMetrics物件
想要獲取FontMetrics,可以通過getFontMetrics()
方法獲取,具體程式碼如下
Paint mPaint = new Paint()
mPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.sp18));
mPaint.setColor(Color.WHITE);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setStrokeWidth(1);
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
複製程式碼
(3)FontMetrics注意事項
使用FontMetrics獲取到的top
、ascent
、descent
、bottom
、leading
成員變數的值,是相對於基線的距離,並不是座標,可以看下下圖,方便理解
可以發現top
、ascent
的值為負數,descent
、bottom
的值為正數,為什麼會這樣呢?因為top
線和ascent線在基線的上方,FontMetrics
物件中的幾個成員變數的值都是表示相對基線的位置。
指定位置寫文字
瞭解了FontMetrics
再結合下圖
可以得到下面的公式:
- top的Y座標 = 基線 + fontMetrics.top
- ascent的Y座標 = 基線 + fontMetrics.ascent
- decent的Y座標 = 基線 + fontMetrics.descent
- bottom的Y座標 = 基線 + fontMetrics.bottom
(1)給定左上方點寫字
根據得出的公式可以計算出基線的Y座標
top的Y座標 = 基線 + fontMetrics.top
基線 = top的Y座標 - fontMetrics.top
實現的程式碼如下
Paint mPaint = new Paint();
mPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.sp18));
mPaint.setStrokeWidth(1);
mPaint.setColor(Color.RED);
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
float baseLine = cy-fontMetrics.top;//cy指定點的Y座標
canvas.drawText("wizardev", 0, baseLine, mPaint);
複製程式碼
(2)給定中間線寫文字
給定中間線寫文字,可以說是自定義view寫文字時用的最多的了,如,將文字寫在圓的正中間,如上圖,圓的中心線將文字平分,這種就是本文說的給定中間線寫文字。文章前面說了,只要確定了基線的位置,文字的位置也就確定了,那麼像這種,怎樣來確定基線的位置呢?這時我們可以藉助其他的幾條線來計算出基線的位置。
如上圖,將top
和center
之間的間距設為A
,將center
和baseline
之間的距離設為B
,將center
和bottom
之間的距離設為C
。這是就可以得出下面的公式
A = C = (bottom - top)/2
B = baseline - center
B = C - (bottom - baseline )
然後根據上文得到的公式:
bottom = fontMetrics.bottom + baseline
top = fontMetrics.top + baseline
可以將最上面的公式修改為:
baseline - center = (fontMetrics.bottom + baseline - fontMetrics.top - baseline) / 2 - (fontMetrics.bottom + baseline - baseline)
最後的到的公式為:
baseline = center - (fontMetrics.bottom - fontMetrics.top) / 2 + fontMetrics.bottom
上面的到的公式就是給出中心線的位置,最後計算出的基線所在位置的公式。
(3)給定底部線線文字
這種情況應該是最簡單的了,直接把給定點的Y座標作為基線的Y座標就行了。
結束語
文章到這裡,已經回答了文章開始的幾個問題,相信閱讀本文之後,你也對自定義View中畫文字有了更清晰的理解。如果仍有什麼疑問,可以在文章下方留言。
本文已由公眾號“AndroidShared”首發