原文地址:Greathfs的Android繪圖系列(五)——繪制文本
這個(gè)系列主要是介紹下Android自定義View和Android繪圖機(jī)制,自己能力有限纲刀,如果在介紹過(guò)程中有什么錯(cuò)誤胶哲,歡迎指正
繪制方法
首先,我們看下繪制文本相關(guān)方法
// 第一種
public void drawText (String text, float x, float y, Paint paint)
public void drawText (String text, int start, int end, float x, float y, Paint paint)
public void drawText (CharSequence text, int start, int end, float x, float y, Paint paint)
public void drawText (char[] text, int index, int count, float x, float y, Paint paint)
// 第二種
public void drawPosText (String text, float[] pos, Paint paint)
public void drawPosText (char[] text, int index, int count, float[] pos, Paint paint)
// 第三種
public void drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint)
public void drawTextOnPath (char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint)
這三種方式有什么區(qū)別嗎锻全?
第一種:基本的繪制方法开瞭,只能指定文本基線(xiàn)位置(基線(xiàn)x默認(rèn)在字符串左側(cè),基線(xiàn)y默認(rèn)在字符串下方)
第二種:可以分別指定每個(gè)文字的位置壳澳。
第三種:根據(jù)路徑繪制文本
這里簡(jiǎn)單解釋下基線(xiàn)
這是我們以前寫(xiě)英文的四線(xiàn)格役拴,其中從上往下數(shù)第三條線(xiàn)就是基線(xiàn)!
這里再說(shuō)下第一種繪制文本的參數(shù)問(wèn)題:第一個(gè)參數(shù)是我們需要繪制的文本钾埂,第四個(gè)參數(shù)是我們的畫(huà)筆河闰,這兩個(gè)不用多說(shuō),主要是第二和第三個(gè)參數(shù)的含義褥紫,這兩個(gè)參數(shù)在不同的情況下的值還是不一樣的姜性,x默認(rèn)是這個(gè)字符串的左邊在屏幕的位置,如果設(shè)置了paint.setTextAlign(Paint.Align.CENTER);那就是字符的中心髓考,y是指定這個(gè)字符baseline在屏幕上的位置部念,大家記住了,不要混淆氨菇,y不是這個(gè)字符中心在屏幕上的位置儡炼,而是baseline在屏幕上的位置!
繪制文本
Paint和第一種繪制方式
前面的文章簡(jiǎn)單介紹過(guò)Paint查蓉,這篇還是要再介紹點(diǎn)屬性乌询,因?yàn)槲覀冊(cè)倮L制文本的時(shí)候,Paint設(shè)置不同屬性豌研,那么繪制出的文本也不同
//基本設(shè)置
paint.setStrokeWidth (2);//設(shè)置畫(huà)筆寬度
paint.setAntiAlias(true); //指定是否使用抗鋸齒功能妹田,如果使用唬党,會(huì)使繪圖速度變慢
paint.setStyle(Paint.Style.FILL);//繪圖樣式,對(duì)于設(shè)文字和幾何圖形都有效
//設(shè)置文字對(duì)齊方式鬼佣,取值:align.CENTER驶拱、align.LEFT或align.RIGHT
paint.setTextAlign(Align.CENTER);
//設(shè)置文字大小
paint.setTextSize(12);
//樣式設(shè)置
paint.setFakeBoldText(true);//設(shè)置是否為粗體文字
paint.setUnderlineText(true);//設(shè)置下劃線(xiàn)
paint.setTextSkewX((float) -0.25);//設(shè)置字體水平傾斜度,普通斜體字是-0.25
paint.setStrikeThruText(true);//設(shè)置帶有刪除線(xiàn)效果
//設(shè)置拉伸
paint.setTextScaleX(2);//只會(huì)將水平方向拉伸晶衷,高度不會(huì)變
OK蓝纲,我們分別看下效果
- 繪圖樣式不同效果
Paint一共有三種樣式:填充,描邊晌纫,填充和描邊
Paint paint=new Paint();
paint.setColor(Color.BLUE); //設(shè)置畫(huà)筆顏色
paint.setStrokeWidth (5);//設(shè)置畫(huà)筆寬度
paint.setAntiAlias(true); //指定是否使用抗鋸齒功能税迷,如果使用,會(huì)使繪圖速度變慢
paint.setTextSize(100);//設(shè)置文字大小
//繪圖樣式設(shè)置為填充
paint.setStyle(Paint.Style.FILL);
canvas.drawText("這是一條測(cè)試數(shù)據(jù)", 20,200, paint);
//繪圖樣式設(shè)置為描邊
paint.setStyle(Paint.Style.STROKE);
canvas.drawText("這是一條測(cè)試數(shù)據(jù)", 20,400, paint);
//繪圖樣式設(shè)置為填充且描邊
paint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawText("這是一條測(cè)試數(shù)據(jù)", 20,600, paint);
效果圖:
- 文字樣式不同效果
Paint paint=new Paint();
paint.setColor(Color.BLUE); //設(shè)置畫(huà)筆顏色
paint.setStrokeWidth (5);//設(shè)置畫(huà)筆寬度
paint.setAntiAlias(true); //指定是否使用抗鋸齒功能缸匪,如果使用翁狐,會(huì)使繪圖速度變慢
paint.setTextSize(100);//設(shè)置文字大小
paint.setStyle(Paint.Style.FILL);
//樣式設(shè)置
//粗體
paint.setFakeBoldText(true);
//下劃線(xiàn)
paint.setUnderlineText(true);
//刪除線(xiàn)
paint.setStrikeThruText(true);
//不傾斜
canvas.drawText("這是一條測(cè)試數(shù)據(jù)",20,200,paint);
//向有傾斜
paint.setTextSkewX((float) -0.25);
canvas.drawText("這是一條測(cè)試數(shù)據(jù)",20,400,paint);
//向左傾斜
paint.setTextSkewX((float) 0.25);
canvas.drawText("這是一條測(cè)試數(shù)據(jù)",20,600,paint);
效果圖
- 其它效果
Paint paint=new Paint();
paint.setColor(Color.BLUE); //設(shè)置畫(huà)筆顏色
paint.setStrokeWidth (5);//設(shè)置畫(huà)筆寬度
paint.setAntiAlias(true); //指定是否使用抗鋸齒功能类溢,如果使用凌蔬,會(huì)使繪圖速度變慢
paint.setTextSize(100);//設(shè)置文字大小
paint.setStyle(Paint.Style.FILL);//繪圖樣式,設(shè)置為填充
//變通樣式字體
canvas.drawText("這是一條測(cè)試數(shù)據(jù)", 20,200, paint);
//水平方向拉伸兩倍
paint.setTextScaleX(2);//只會(huì)將水平方向拉伸闯冷,高度不會(huì)變
canvas.drawText("這是一條測(cè)試數(shù)據(jù)", 20,400, paint);
//寫(xiě)在同一位置,不同顏色,看下高度是否看的不變
paint.setTextScaleX(1);//先還原拉伸效果
canvas.drawText("這是一條測(cè)試數(shù)據(jù)", 20,600, paint);
paint.setColor(Color.RED);
paint.setTextScaleX(2);//重新設(shè)置拉伸效果
canvas.drawText("這是一條測(cè)試數(shù)據(jù)", 20,600, paint);
效果圖
OK砂心,Paint關(guān)于繪制文本這部分的屬性就介紹這些
第二種繪制方式
public void drawPosText (String text, float[] pos, Paint paint)
public void drawPosText (char[] text, int index, int count, float[] pos, Paint paint)
解釋下參數(shù): String text:要繪制的文字 char[] text:要繪制的文字?jǐn)?shù)組 int index::第一個(gè)要繪制的文字的索引 int count:要繪制的文字的個(gè)數(shù),用來(lái)算最后一個(gè)文字的位置蛇耀,從第一個(gè)繪制的文字開(kāi)始算起 float[] pos:每個(gè)字體的位置辩诞,同樣兩兩一組,如{x1,y1,x2,y2,x3,y3……}
Paint paint=new Paint();
paint.setColor(Color.BLUE); //設(shè)置畫(huà)筆顏色
paint.setStrokeWidth (5);//設(shè)置畫(huà)筆寬度
paint.setAntiAlias(true); //指定是否使用抗鋸齒功能纺涤,如果使用译暂,會(huì)使繪圖速度變慢
paint.setTextSize(100);//設(shè)置文字大小
//繪圖樣式設(shè)置為填充
paint.setStyle(Paint.Style.FILL);
canvas.drawPosText("Hello",new float[]{
100,100, // 第一個(gè)字符位置
200,200, // 第二個(gè)字符位置
300,300, // ...
400,400,
500,500
},paint);
效果圖
第三種繪制方式
public void drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint)
public void drawTextOnPath (char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint)
解釋下參數(shù) float hOffset : 與路徑起始點(diǎn)的水平偏移距離 float vOffset : 與路徑中心的垂直偏移量
這種設(shè)計(jì)到Path(路徑),這個(gè)后面會(huì)寫(xiě)撩炊,就先看看小例子了解下就行
Paint paint=new Paint();
paint.setColor(Color.RED); //設(shè)置畫(huà)筆顏色
paint.setStrokeWidth (5);//設(shè)置畫(huà)筆寬度
paint.setAntiAlias(true); //指定是否使用抗鋸齒功能外永,如果使用,會(huì)使繪圖速度變慢
paint.setTextSize(45);//設(shè)置文字大小
paint.setStyle(Paint.Style.STROKE);//繪圖樣式拧咳,設(shè)置為填充
String string="Hello World";
//先創(chuàng)建兩個(gè)相同的圓形路徑伯顶,并先畫(huà)出兩個(gè)路徑原圖
Path circlePath=new Path();
circlePath.addCircle(220,200, 180, Path.Direction.CCW);//逆向繪制
canvas.drawPath(circlePath, paint);//繪制出路徑原形
Path circlePath2=new Path();
circlePath2.addCircle(750,200, 180, Path.Direction.CCW);
canvas.drawPath(circlePath2, paint);//繪制出路徑原形
paint.setColor(Color.GREEN);
//hoffset、voffset參數(shù)值全部設(shè)為0骆膝,看原始狀態(tài)是怎樣的
canvas.drawTextOnPath(string, circlePath, 0, 0, paint);
//第二個(gè)路徑祭衩,改變hoffset、voffset參數(shù)值
canvas.drawTextOnPath(string, circlePath2, 80, 30, paint);
效果圖
drawText()中原點(diǎn)坐標(biāo)(x,y)
我們知道阅签,我們?cè)赿rawText(text, x, y, paint)中傳進(jìn)去的原點(diǎn)坐標(biāo)(x,y);其中掐暮,y表示的基線(xiàn)的位置。那x代表什么呢政钟?x代表所要繪制文字所在矩形的相對(duì)位置劫乱。相對(duì)位置就是指指定點(diǎn)(x,y)在在所要繪制矩形的位置织中。我們知道所繪制矩形的縱坐標(biāo)是由Y值來(lái)確定的,而相對(duì)x坐標(biāo)的位置衷戈,只有左狭吼、中、右三個(gè)位置了殖妇。也就是所繪制矩形可能是在x坐標(biāo)的左側(cè)繪制刁笙,也有可能在x坐標(biāo)的中間,也有可能在x坐標(biāo)的右側(cè)谦趣。
定義在x坐標(biāo)在所繪制矩形相對(duì)位置的函數(shù)是:
/**
* 其中Align的取值為:Panit.Align.LEFT,Paint.Align.CENTER,Paint.Align.RIGHT
*/
Paint::setTextAlign(Align align);
(1)setTextAlign(Paint.Align.LEFT)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint=new Paint();
//設(shè)置基本屬性
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(120);
paint.setTextAlign(Paint.Align.LEFT);
// paint.setTextAlign(Paint.Align.RIGHT);
// paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("abcdefghijk", 200, 200, paint);
//繪制坐標(biāo)系
paint.setColor(Color.BLUE);
canvas.drawLine(0, 200, getWidth(), 200, paint);
canvas.drawLine(200, 0, 200, getHeight(), paint);
}
看完效果圖疲吸,我們應(yīng)該知道了原點(diǎn)(x,y)在矩形的左側(cè),即矩形從(x,y)的點(diǎn)開(kāi)始繪制
(2)setTextAlign(Paint.Align.RIGHT)
把上面的代碼改一改
//畫(huà)基線(xiàn)
//設(shè)置基本屬性
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(120);
// paint.setTextAlign(Paint.Align.LEFT);
paint.setTextAlign(Paint.Align.RIGHT);
// paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("abcdefghijk", 200, 200, paint);
//繪制坐標(biāo)系
paint.setColor(Color.BLUE);
canvas.drawLine(0, 200, getWidth(), 200, paint);
canvas.drawLine(200, 0, 200, getHeight(), paint);
原點(diǎn)(x,y)應(yīng)在所要繪制矩形的右側(cè)前鹅,也就是說(shuō)正個(gè)矩形都在(0,200)的左側(cè)摘悴,所以我們看到的是什么都沒(méi)有。
(3)setTextAlign(Paint.Align.RIGHT)
Paint paint=new Paint();
//設(shè)置基本屬性
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(120);
// paint.setTextAlign(Paint.Align.LEFT);
// paint.setTextAlign(Paint.Align.RIGHT);
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("abcdefghijk", 200, 200, paint);
//繪制坐標(biāo)系
paint.setColor(Color.BLUE);
canvas.drawLine(0, 200, getWidth(), 200, paint);
原點(diǎn)(x,y)就在所要繪制文字所在矩形區(qū)域的正中間舰绘,換句話(huà)說(shuō)蹂喻,系統(tǒng)會(huì)根據(jù)(x,y)的位置和文字所在矩形大小,會(huì)計(jì)算出當(dāng)前開(kāi)始繪制的點(diǎn),以使原點(diǎn)(x,y)正好在所要繪制的矩形的正中間捂寿。
FontMetrics
繪制text的其他線(xiàn)
除了基線(xiàn)口四,系統(tǒng)在繪制Text時(shí),還是有其它線(xiàn)的秦陋,如下圖
從圖中可以知道蔓彩,除了基線(xiàn),還有另外的四條線(xiàn)驳概,它們分別是 top赤嚼,ascent,descent和bottom顺又,它們的含義分別為:
- top:可繪制的最高高度所在線(xiàn)
- bottom:可繪制的最低高度所在線(xiàn)
- ascent :系統(tǒng)建議的更卒,繪制單個(gè)字符時(shí),字符應(yīng)當(dāng)?shù)淖罡吒叨人诰€(xiàn)
- descent:系統(tǒng)建議的待榔,繪制單個(gè)字符時(shí)逞壁,字符應(yīng)當(dāng)?shù)淖畹透叨人诰€(xiàn)
FontMetrics介紹
既然還有四條線(xiàn),那這些線(xiàn)的位置怎么計(jì)算呢锐锣?
Android提供了一個(gè)類(lèi):FontMetrics腌闯,這個(gè)類(lèi)里面對(duì)應(yīng)有成員變量
它們的計(jì)算方法如下
ascent = ascent線(xiàn)的y坐標(biāo) - baseline線(xiàn)的y坐標(biāo);//負(fù)數(shù)
descent = descent線(xiàn)的y坐標(biāo) - baseline線(xiàn)的y坐標(biāo)雕憔;//正數(shù)
top = top線(xiàn)的y坐標(biāo) - baseline線(xiàn)的y坐標(biāo)姿骏;//負(fù)數(shù)
bottom = bottom線(xiàn)的y坐標(biāo) - baseline線(xiàn)的y坐標(biāo);//正數(shù)
leading = top線(xiàn)的y坐標(biāo) - ascent線(xiàn)的y坐標(biāo)斤彼;//負(fù)數(shù)
FontMetrics的這幾個(gè)變量的值都是以baseLine為基準(zhǔn)的分瘦,對(duì)于ascent來(lái)說(shuō)蘸泻,baseline線(xiàn)在ascent線(xiàn)之下,所以必然baseline的y值要大于ascent線(xiàn)的y值嘲玫,所以ascent變量的值是負(fù)的悦施。其他幾個(gè)同理。
同樣我們可以推算出:
ascent線(xiàn)Y坐標(biāo) = baseline線(xiàn)的y坐標(biāo) + fontMetric.ascent去团;
descent線(xiàn)Y坐標(biāo) = baseline線(xiàn)的y坐標(biāo) + fontMetric.descent抡诞;
top線(xiàn)Y坐標(biāo) = baseline線(xiàn)的y坐標(biāo) + fontMetric.top;
bottom線(xiàn)Y坐標(biāo) = baseline線(xiàn)的y坐標(biāo) + fontMetric.bottom土陪;
繪制ascent昼汗,descent,top鬼雀,bottom線(xiàn)
獲取FontMetrics實(shí)例
Paint.FontMetrics fontMetrics=paint.getFontMetrics();
Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
區(qū)別:得到對(duì)象的成員變量的值一個(gè)為float類(lèi)型顷窒,一個(gè)為int類(lèi)型。
繪制
int baseLineY = 200;
int baseLineX = 0 ;
Paint paint = new Paint();
//寫(xiě)文字
paint.setColor(Color.GREEN);
paint.setTextSize(120); //以px為單位
paint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("abcdefghijk", baseLineX, baseLineY, paint);
//計(jì)算各線(xiàn)在位置
Paint.FontMetrics fm = paint.getFontMetrics();
float ascent = baseLineY + fm.ascent;
float descent = baseLineY + fm.descent;
float top = baseLineY + fm.top;
float bottom = baseLineY + fm.bottom;
//畫(huà)基線(xiàn)
paint.setColor(Color.RED);
canvas.drawLine(baseLineX, baseLineY, 3000, baseLineY, paint);
//畫(huà)top
paint.setColor(Color.BLUE);
canvas.drawLine(baseLineX, top, 3000, top, paint);
//畫(huà)ascent
paint.setColor(Color.GREEN);
canvas.drawLine(baseLineX, ascent, 3000, ascent, paint);
//畫(huà)descent
paint.setColor(Color.BLACK);
canvas.drawLine(baseLineX, descent, 3000, descent, paint);
//畫(huà)bottom
paint.setColor(Color.RED);
canvas.drawLine(baseLineX, bottom, 3000, bottom, paint);
獲取文字寬度源哩、高度和最小矩形
獲取文字寬度
String text="abcdefghijk";
//文字寬度
float width = mPaint.measureText(text);
獲取文字高度
float top = fontMetrics.top + baseLineY;
float bottom = fontMetrics.bottom + baseLineY;
//文字高度
float height= bottom - top; //注意top為負(fù)數(shù)
//文字中點(diǎn)y坐標(biāo)
float center = (bottom - top) / 2;
獲取文字最小矩形
/**
* 獲取指定字符串所對(duì)應(yīng)的最小矩形鞋吉,以(0,0)點(diǎn)所在位置為基線(xiàn)
* @param text 要測(cè)量最小矩形的字符串
* @param start 要測(cè)量起始字符在字符串中的索引
* @param end 所要測(cè)量的字符的長(zhǎng)度
* @param bounds 接收測(cè)量結(jié)果
*/
public void getTextBounds(String text, int start, int end, Rect bounds);
例子:
String text = "abcdefghijk";
Paint paint = new Paint();
//設(shè)置paint
paint.setTextSize(120); //以px為單位
Rect minRect = new Rect();
paint.getTextBounds(text,0,text.length(),minRect);
drawText實(shí)現(xiàn)中文垂直居中
drawText里的origin是以baseline為基準(zhǔn)的璧疗,直接以目標(biāo)矩形的bottom傳進(jìn)drawText坯辩,字符位置會(huì)偏下馁龟。
Rect rect = new Rect(50, 50, 1000, 200);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(3);
paint.setTextSize(80);
String testString = "abcdefghijk";
paint.setColor(Color.BLUE);
canvas.drawRect(rect, paint);
paint.setColor(Color.RED);
canvas.drawText(testString, rect.left, rect.bottom, paint);
所以我們需要自己計(jì)算下基線(xiàn)的位置
這是上面的一張圖崩侠,我們知道,我們繪制的Text在FontMetrics.top和FontMetrics.bottom線(xiàn)之間坷檩,F(xiàn)ontMetrics.top的數(shù)值是個(gè)負(fù)數(shù)却音,其絕對(duì)值就是字體繪制邊界到baseline的距離。所以如果是把文字畫(huà)在 FontMetrics高度的矩形中矢炼, drawText就應(yīng)該傳入 -FontMetrics.top系瓢。要畫(huà)在targetRect的居中位置,baseline的計(jì)算公式就是:
targetRect.centerY() - (FontMetrics.bottom - FontMetrics.top) / 2 - FontMetrics.top
優(yōu)化后即:
(targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2
Rect targetRect = new Rect(50, 50, 1000, 200);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(3);
paint.setTextSize(80);
String testString = "abcdefghijk";
paint.setColor(Color.BLUE);
canvas.drawRect(targetRect, paint);
paint.setColor(Color.RED);
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(testString, targetRect.centerX(), baseline, paint);
結(jié)尾
參考文章: