一朋沮、概述
1. 四線格與基線
小時(shí)候何荚,我們?cè)趧傞_(kāi)始學(xué)習(xí)寫(xiě)字母時(shí)囱淋,用的本子是四線格的,我們必須把字母按照規(guī)則寫(xiě)在四線格內(nèi)餐塘。
比如:
那么問(wèn)題來(lái)了妥衣,在canvas在利用drawText繪制文字時(shí),也是有規(guī)則的戒傻,這個(gè)規(guī)則就是baseline(基線)税手!
我們先來(lái)看一下什么是基線:
對(duì)比以上兩圖,可見(jiàn)基線就是四線格中的第三條線需纳!也就是說(shuō)芦倒,只要基線的位置定了,那文字的位置必然是定了的候齿!
2. canvas.drawText()
(1)canvas.drawText()與基線
下面我們來(lái)看看canvas.drawText()這個(gè)函數(shù)
/**
* text:要繪制的文字
* x:繪制原點(diǎn)x坐標(biāo)
* y:繪制原點(diǎn)y坐標(biāo)
* paint:用來(lái)做畫(huà)的畫(huà)筆
*/
public void drawText(String text, float x, float y, Paint paint)
上面這個(gè)構(gòu)造函數(shù)是最常用的drawText方法熙暴,傳進(jìn)去一個(gè)String對(duì)象就能畫(huà)出對(duì)應(yīng)的文字。
但這里有兩個(gè)參數(shù)需要非常注意慌盯,表示原點(diǎn)坐標(biāo)的x和y.很多同學(xué)可能會(huì)認(rèn)為,這里傳進(jìn)去的原點(diǎn)參數(shù)(x,y)是所在繪制文字所在矩形的左上角的點(diǎn)掂器。但實(shí)際上并不是亚皂!比如,我們上面如果要畫(huà)“matt’s blog”這幾個(gè)字国瓮,這個(gè)原點(diǎn)坐標(biāo)應(yīng)當(dāng)是下圖中綠色小點(diǎn)的位置
(2)實(shí)例
下面我們就舉個(gè)例子來(lái)看一下drawText中灭必,原點(diǎn)坐標(biāo)(x,y)的位置。
新建一個(gè)工程乃摹,創(chuàng)建一個(gè)MainActivity,然后創(chuàng)建一個(gè)CustomTextView繼承View
重寫(xiě)onDraw函數(shù)
//重寫(xiě)onDraw函數(shù)禁漓,自定義一個(gè)基線,然后利用drawText畫(huà)出來(lái)
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int baseLineX = 0 ;
int baseLineY = 200;
//畫(huà)基線
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);
//畫(huà)文字
paint.setColor(Color.BLUE);
paint.setTextSize(120); //以px為單位
canvas.drawText("matt's blog", baseLineX, baseLineY, paint);
}
在這里孵睬,先定義drawText原點(diǎn)的位置:(0播歼,200)
首先,我們把(0,200)所在的這條橫線畫(huà)出來(lái)掰读,所以我先畫(huà)了一條線從點(diǎn)坐標(biāo)為(0,200)到點(diǎn)坐標(biāo)為(2500,200)的一條直線
然后利用canvas.drawText以(0秘狞,200)為原點(diǎn)畫(huà)出文字
在main.xml添加自定義view的引用
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/app_bar_main"
tools:context="cn.addapp.customviews.MainActivity">
android:layout_width="match_parent"
android:layout_height="match_parent"/>
效果圖如下:
結(jié)論:
1. drawText是中的參數(shù)y是基線的位置叭莫。
2. 一定要清楚的是,只要x坐標(biāo)烁试、基線位置雇初、文字大小確定以后,文字的位置就是確定的了减响。
3. paint.setTextAlign(Paint.Align.XXX)
在上面我們講了靖诗,drawText()函數(shù)中的Y坐標(biāo)表示所要繪制文字的基線所在位置。從上面的例子支示,我們可以看到呻畸,我們繪圖結(jié)果是在X坐標(biāo)的右邊開(kāi)始繪制的,但這并不是必然的結(jié)果悼院。
我們來(lái)看一張圖:
我們知道伤为,我們?cè)赿rawText(text, x, y, paint)中傳進(jìn)去的源點(diǎn)坐標(biāo)(x,y);其中,y表示的基線的位置据途。那x代表什么呢绞愚?從上面的例子運(yùn)行結(jié)果來(lái)看,應(yīng)當(dāng)是文字開(kāi)始繪制的地方颖医。
并不是所要繪制文字所在矩形的相對(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);
我們?cè)賮?lái)重新看一下上面的例子,當(dāng)我們?cè)O(shè)置為不同的值時(shí)罐脊,繪制結(jié)果是怎樣的定嗓。
同樣的代碼,我們加上paint.setTextAlign()來(lái)設(shè)置相對(duì)位置來(lái)看看結(jié)果萍桌。
(1)setTextAlign(Paint.Align.LEFT)
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int baseLineY = 200;
int baseLineX = 0 ;
//畫(huà)基線
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);
//畫(huà)文字
paint.setColor(Color.BLUE);
paint.setTextSize(120); //以px為單位
paint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("matt's blog", baseLineX, baseLineY, paint);
}
運(yùn)行結(jié)果如下:
可見(jiàn)宵溅,原點(diǎn)(x,y)在矩形的左側(cè),即矩形從(x,y)的點(diǎn)開(kāi)始繪制
(2)setTextAlign(Paint.Align.CENTER)
同樣的代碼上炎,把相對(duì)位置設(shè)置為:setTextAlign(Paint.Align.CENTER)
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int baseLineY = 200;
int baseLineX = 0 ;
//畫(huà)基線
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);
//畫(huà)文字
paint.setColor(Color.BLUE);
paint.setTextSize(120); //以px為單位
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("matt's blog", baseLineX, baseLineY, paint);
}
運(yùn)行結(jié)果如下:
所以原點(diǎn)(x,y)就在所要繪制文字所在矩形區(qū)域的正中間恃逻,換句話說(shuō),系統(tǒng)會(huì)根據(jù)(x,y)的位置和文字所在矩形大小,會(huì)計(jì)算出當(dāng)前開(kāi)始繪制的點(diǎn)辛块。以使原點(diǎn)(x,y)正好在所要繪制的矩形的正中間畔派。
(3)setTextAlign(Paint.Align.RIGHT)
同樣的代碼,把相對(duì)位置設(shè)置為:setTextAlign(Paint.Align.RIGHT)
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int baseLineY = 200;
int baseLineX = 0 ;
//畫(huà)基線
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);
//畫(huà)文字
paint.setColor(Color.BLUE);
paint.setTextSize(120); //以px為單位
paint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText("matt's blog", baseLineX, baseLineY, paint);
}
運(yùn)行結(jié)果如下:
所以原點(diǎn)(x,y)應(yīng)當(dāng)在所要繪制矩形的右側(cè)润绵,在上面的代碼中线椰,也就是說(shuō)整個(gè)矩形都在(0,200)的左側(cè),所以我們看到的是什么都沒(méi)有尘盼。
(4)注意
下面憨愉,我們?cè)倏匆粋€(gè)例子:
我們只寫(xiě)一個(gè)大寫(xiě)字母 M,然后將其相對(duì)位置設(shè)置為paint.setTextAlign(Paint.Align.CENTER)
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int baseLineY = 200;
int baseLineX = 0 ;
//畫(huà)基線
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);
//畫(huà)文字
paint.setColor(Color.BLUE);
paint.setTextSize(120); //以px為單位
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("M", baseLineX, baseLineY, paint);
}
運(yùn)行結(jié)果如下:
我們可以看到字母M在原點(diǎn)(0卿捎,200)的正中間配紫。
這也就是我們要強(qiáng)調(diào)的:相對(duì)位置是根據(jù)所要繪制文字所在矩形來(lái)計(jì)算的。
二午阵、drawText的四線格與FontMetrics
1躺孝、Text的繪圖四線格
前面我們講了基線,其實(shí)除了基線底桂,系統(tǒng)在繪制Text時(shí)植袍,還是有其它線的,我們來(lái)看個(gè)圖:
除了基線以外籽懦,如上圖所示于个,另外還有四條線,分別是ascent,descent,top,bottom暮顺,他們的意義分別是:
ascent: 系統(tǒng)建議的厅篓,繪制單個(gè)字符時(shí),字符應(yīng)當(dāng)?shù)淖罡吒叨人诰€
descent:系統(tǒng)建議的捶码,繪制單個(gè)字符時(shí)羽氮,字符應(yīng)當(dāng)?shù)淖畹透叨人诰€
top: 可繪制的最高高度所在線
bottom: 可繪制的最低高度所在線
單從這幾個(gè)定義,大家可能還是搞不清這幾值到底是什么意義宙项。我們來(lái)看一下電視的顯示乏苦。用過(guò)視頻處理工具的同學(xué)(比如premiere,AE,繪聲繪影等),應(yīng)該都會(huì)知道,在制作視頻時(shí)尤筐,視頻顯示位置都會(huì)有一個(gè)安全區(qū)域框,如下圖所示:
如上圖所示洞就,黑色部分表示電視屏幕盆繁,紅色框就表示安全區(qū)域框。
這個(gè)安全框是用來(lái)干嘛的旬蟋?這個(gè)安全框就是系統(tǒng)推薦給我們的顯示區(qū)域油昂,雖然說(shuō)我們可以講電視屏幕是每個(gè)區(qū)域都是可以顯示圖像的,但由于制式的不同,每個(gè)國(guó)家的屏幕大小并不一定我們這里的屏幕大小一致冕碟,當(dāng)遇到不一致時(shí)拦惋,就會(huì)裁剪。但系統(tǒng)給我們推薦的顯示區(qū)域是無(wú)論哪種制式都是可以完整顯示出來(lái)的安寺,所以我們?cè)谥谱饕曨l時(shí)厕妖,盡量要把要顯示的圖像放在所推薦的顯示區(qū)域內(nèi)。
同樣挑庶,在這里言秸,我們?cè)诶L制文字時(shí),ascent是推薦的繪制文字的最高高度迎捺,就表示在繪制文字時(shí)举畸,盡力要在這個(gè)最高高度以下繪制文字。descent是推薦的繪制文字的最底高度線凳枝,同樣表示是在繪制文字時(shí)盡量在這個(gè)descent線以上來(lái)繪制文字抄沮。而top線則指該文字可以繪制的最高高度線,bottom則是表示該文字可以繪制的最低高度線岖瑰。ascent,descent是系統(tǒng)建議上的繪制高度叛买,而top,bottom則是物理上屏幕最高,最低可以畫(huà)的高度值锭环。他們的差別與我們上面說(shuō)的視頻處理的安全框和屏幕的道理是一樣的聪全。
2、FontMetrics
(1)辅辩、fontMetrics概述
上面我們講了难礼,系統(tǒng)在畫(huà)文字時(shí)的五條線,baseline玫锋、ascent蛾茉、descent、top撩鹿、bottom我們知道baseline的位置是我們?cè)跇?gòu)造drawText()時(shí)的參數(shù)y來(lái)決定的谦炬,那ascent,descent,top,bottom這些線的位置要怎么計(jì)算出來(lái)呢?
Android給我們提供了一個(gè)類(lèi):FontMetrics节沦,它里面有四個(gè)成員變量:
FontMetrics::ascent;
FontMetrics::descent;
FontMetrics::top;
FontMetrics::bottom;
他們的意義與值的計(jì)算方法分別如下:
ascent = ascent線的y坐標(biāo) - baseline線的y坐標(biāo)键思;
descent = descent線的y坐標(biāo) - baseline線的y坐標(biāo);
top = top線的y坐標(biāo) - baseline線的y坐標(biāo)甫贯;
bottom = bottom線的y坐標(biāo) - baseline線的y坐標(biāo)吼鳞;
我們?cè)賮?lái)看這個(gè)圖:
從這個(gè)圖中,我們先說(shuō)明兩點(diǎn)叫搁,然后再回過(guò)頭來(lái)看上面的公式:
1赔桌、X軸供炎,Y軸的正方向走向是X軸向右是正方向,Y軸向下是正方向疾党,所以越往右X坐標(biāo)越大音诫,越往下Y坐標(biāo)越大!
2雪位、大家千萬(wàn)不要將FontMetrics中的ascent,descent,top,bottom與現(xiàn)實(shí)中的ascent,descent,top,bottom所在線混淆竭钝!
這幾條線是真實(shí)存在的,而FontMetrics中的ascent,descent,top,bottom這個(gè)變量的值就是用來(lái)計(jì)算這幾條線的位置的茧泪。下面我們將看到如何利用這幾個(gè)變量來(lái)計(jì)算這幾條線的位置蜓氨。
看完這個(gè)圖,我們?cè)僦匦抡f(shuō)說(shuō)這幾個(gè)公式队伟,我們拿一個(gè)來(lái)說(shuō)吧穴吹,其它都是相同的道理。
ascent = ascent線的y坐標(biāo) - baseline線的y坐標(biāo)嗜侮;
FontMetrics的這幾個(gè)變量的值都是以baseline為基準(zhǔn)的港令,對(duì)于ascent來(lái)說(shuō),baseline線在ascent線之下锈颗,所以必然baseline的y值要大于ascent線的y值顷霹,所以ascent變量的值是負(fù)的。
同理击吱,對(duì)于descent而言:
descent = descent線的y坐標(biāo) - baseline線的y坐標(biāo)淋淀;
descent線在baseline線之下,所以必然descent線的y坐標(biāo)要大于baseline線的y坐標(biāo)覆醇,所以descent變量的值必然是正數(shù)朵纷。
(2)、得到Text四線格的各線位置
下面永脓,我們就來(lái)看看如何通過(guò)這些變量來(lái)得到對(duì)應(yīng)線所在位置吧袍辞。
我們先列出來(lái)一個(gè)公式:
ascent線Y坐標(biāo) = baseline線Y坐標(biāo) + fontMetric.ascent;
推算過(guò)程如下:
因?yàn)閍scent線的Y坐標(biāo)等于baseline線的Y坐標(biāo)減去從baseline線到ascent線的這段距離。
也就是:(|fontMetric.ascent|表示取絕對(duì)值)
ascent線Y坐標(biāo) = baseline線Y坐標(biāo) - |fontMetric.ascent|;
又因?yàn)閒ontMetric.ascent是負(fù)值常摧,所以:
ascent線Y坐標(biāo) = baseline線Y坐標(biāo) - |fontMetric.ascent|;
ascent線Y坐標(biāo) = baseline線Y坐標(biāo) - ( -fontMetric.ascent);
ascent線Y坐標(biāo) = baseline線Y坐標(biāo) + fontMetric.ascent;
這就是整個(gè)推算過(guò)程搅吁,沒(méi)什么難度,同理可以得到:
ascent線Y坐標(biāo) = baseline線的y坐標(biāo) + fontMetric.ascent落午;
descent線Y坐標(biāo) = baseline線的y坐標(biāo) + fontMetric.descent谎懦;
top線Y坐標(biāo) = baseline線的y坐標(biāo) + fontMetric.top;
bottom線Y坐標(biāo) = baseline線的y坐標(biāo) + fontMetric.bottom溃斋;
(3)党瓮、獲取FontMetrics對(duì)象
獲取FontMetrics對(duì)象是根據(jù)paint對(duì)象來(lái)獲取的:
Paint paint = new Paint();
Paint.FontMetrics fm = paint.getFontMetrics();
Paint.FontMetricsInt fmInt = paint.getFontMetricsInt();
從這里可以看到,通過(guò)paint.getFontMetrics()得到對(duì)應(yīng)的FontMetrics對(duì)象盐类。這里還有另外一個(gè)FontMetrics同樣的類(lèi)叫做FontMetricsInt寞奸,它的意義與FontMetrics完全相同,只是得到的值的類(lèi)型不一樣而已在跳,F(xiàn)ontMetricsInt中的四個(gè)成員變量的值都是Int類(lèi)型枪萄,而FontMetrics得到的四個(gè)成員變量的值則都是float類(lèi)型的。
(4)猫妙、實(shí)例:計(jì)算Text四線格位置
在這個(gè)例子中瓷翻,我們先寫(xiě)一行字,然后畫(huà)出這行字中的top線割坠,ascent線齐帚,baseline線,descent線和bottom線彼哼。
我們直接上完整代碼:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int baseLineY = 200;
int baseLineX = 0 ;
Paint paint = new Paint();
//寫(xiě)文字
paint.setColor(Color.BLUE);
paint.setTextSize(120); //以px為單位
paint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("matt's blog", baseLineX, baseLineY, paint);
//計(jì)算各線在位置
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à)基線
paint.setColor(Color.parseColor("#FF0000"));
paint.setFakeBoldText(true);
paint.setStrokeWidth(3);
canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);
//畫(huà)top
paint.setColor(Color.parseColor("#196F3D"));
canvas.drawLine(baseLineX, top, 2500, top, paint);
//畫(huà)ascent
paint.setColor(Color.parseColor("#1A5276"));
canvas.drawLine(baseLineX, ascent, 2500, ascent, paint);
//畫(huà)descent
paint.setColor(Color.parseColor("#F1C40F"));
canvas.drawLine(baseLineX, descent, 2500, descent, paint);
//畫(huà)bottom
paint.setColor(Color.parseColor("#33FF00"));
canvas.drawLine(baseLineX, bottom, 2500, bottom, paint);
}
這段代碼中对妄,總共分為三部分,寫(xiě)文字敢朱、計(jì)算各線所在位置剪菱、畫(huà)出各條線;我們逐段來(lái)講
先看寫(xiě)文字:
int baseLineY = 200;
int baseLineX = 0 ;
Paint paint = new Paint();
//寫(xiě)文字
paint.setColor(Color.BLUE);
paint.setTextSize(120); //以px為單位
paint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("matt's blog", baseLineX, baseLineY, paint);
有關(guān)drawText的問(wèn)題我們已經(jīng)講過(guò)拴签,在這段代碼中孝常,我們需要注意的是兩點(diǎn):
1、drawText中的參數(shù)y是基線的位置
2蚓哩、paint.setTextAlign(Paint.Align.LEFT);設(shè)置的相對(duì)位置為构灸,指定的原點(diǎn)(0,200)在繪制矩形的左側(cè)。換句話說(shuō)岸梨,所繪制的文字所在矩形在(0,200)點(diǎn)的右側(cè)
然后是計(jì)算各各線的y坐標(biāo)位置:
//計(jì)算各線在位置
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;
首先喜颁,利用 paint.getFontMetrics()得到FontMetrics的實(shí)例,然后利用我們上面的公式即可得到各條線的y坐標(biāo)盛嘿。
最后就是利用這些y坐標(biāo)畫(huà)出這些線了洛巢,很簡(jiǎn)單,就是drawLine的使用次兆,難度不大稿茉,就不再細(xì)講。
三芥炭、所繪文字寬度漓库、高度和最小矩形獲取
這部分,我們將講解如何獲取所繪制字符串所占區(qū)域的高度园蝠、寬度和僅包裹字符串的最小矩形渺蒿。我們來(lái)看張圖來(lái)講述下他們的意義:
在這張圖中,文字底部的綠色框就是所繪制字符串所占據(jù)的大小彪薛。我們要求的寬度和高度也就是這個(gè)綠色框的寬度和高度茂装。
從圖中也可以看到怠蹂,紅色框部分,它的寬和高緊緊包圍著字符串少态,所以紅色框就是我們要求的最小矩形城侧。即能包裹字符串的最小矩形。
1彼妻、字符串所占高度和寬度
(1)嫌佑、高度
字符串所占高度很容易得到,直接用bottom線所在位置的Y坐標(biāo)減去top線所在位置的Y坐標(biāo)就是字符串所占的高度:
代碼如下:
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
int top = baseLineY + fm.top;
int bottom = baseLineY + fm.bottom;
//所占高度
int height = bottom - top;
(2)侨歉、寬度
寬度是非常容易得到的屋摇,直接利用下面的函數(shù)就可以得到
int width = paint.measureText(String text);
使用示例如下:
String text = "matt's blog";
Paint paint = new Paint();
//設(shè)置paint
paint.setTextSize(120); //以px為單位
//獲取寬度
int width = (int)paint.measureText(text);
(3)、最小矩形
1幽邓、概述
要獲取最小矩形炮温,也是通過(guò)系統(tǒng)函數(shù)來(lái)獲取的,函數(shù)及意義如下:
/**
* 獲取指定字符串所對(duì)應(yīng)的最小矩形颊艳,以(0茅特,0)點(diǎ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);
我們簡(jiǎn)單展示下使用代碼及結(jié)果:
String text = "matt's blog";
Paint paint = new Paint();
//設(shè)置paint
paint.setTextSize(120); //以px為單位
Rect rect = new Rect();
paint.getTextBounds(text,0,text.length(),rect);
Log.e("matt",rect.toShortString());
在這段代碼中,首先設(shè)置字體大小棋枕,然后利用paint.getTextBounds()得到最小矩形白修,最后,我將其打印出來(lái)
結(jié)果如下:
可以看到這個(gè)矩形的左上角位置為(8重斑,-90)兵睛,右下角的位置為(580,25)窥浪;
大家可能會(huì)有疑問(wèn)祖很,為什么左上角的Y坐標(biāo)是個(gè)負(fù)數(shù)?從代碼中漾脂,我們也可以看到假颇,我們并沒(méi)有給getTextBounds()傳遞基線位置。那它就是以(0骨稿,0)為基線來(lái)得到這個(gè)最小矩形的笨鸡!所以這個(gè)最小矩形的位置就是以(0,0)為基線的結(jié)果坦冠!
2形耗、得到最小矩形的實(shí)際位置
我們先來(lái)看一個(gè)原理:
在上面這個(gè)圖中,我們將黑色矩形平行下移距離Y(黃色線依照的是基線的位置)辙浑,那么平移后的左上角點(diǎn)的y坐標(biāo)就是 y2 = y1 + Y;
同樣的道理激涤,由于paint.getTextBounds()得到最小矩形的基線是y = 0;那我們直接將這個(gè)矩形移動(dòng)baseline的距離就可以得到這個(gè)矩形實(shí)際應(yīng)當(dāng)在的位置了。
所以矩形應(yīng)當(dāng)所在實(shí)際位置的坐標(biāo)是:
Rect minRect = new Rect();
paint.getTextBounds(text,0,text.length(),minRect);
//最小矩形判呕,實(shí)際top位置
int minTop = minRect.top + baselineY;
//最小矩形倦踢,實(shí)際bottom位置
int minBottom = minRect.bottom + baselineY;
3送滞、實(shí)例
下面我們就舉個(gè)例子來(lái)看一下我們列舉的這幾個(gè)函數(shù)的使用方法
效果圖與這一節(jié)開(kāi)篇時(shí)的效果圖是一樣的,如下:
我們先看一下完整的代碼硼一,然后再細(xì)講
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text = "matt's blog";
int baseLineY = 200;
int baseLineX = 0 ;
//設(shè)置paint
Paint paint = new Paint();
paint.setTextSize(120); //以px為單位
paint.setTextAlign(Paint.Align.LEFT);
//畫(huà)text所占的區(qū)域
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
int top = baseLineY + fm.top;
int bottom = baseLineY + fm.bottom;
int width = (int)paint.measureText(text);
Rect rect = new Rect(baseLineX,top,baseLineX+width,bottom);
paint.setColor(Color.GREEN);
canvas.drawRect(rect,paint);
//畫(huà)最小矩形
Rect minRect = new Rect();
paint.getTextBounds(text,0,text.length(),minRect);
minRect.top = baseLineY + minRect.top;
minRect.bottom = baseLineY + minRect.bottom;
paint.setColor(Color.RED);
canvas.drawRect(minRect,paint);
//寫(xiě)文字
paint.setColor(Color.WHITE);
canvas.drawText(text, baseLineX, baseLineY, paint);
}
這段代碼總共分為四部分:設(shè)置paint,畫(huà)字符串所占據(jù)矩形累澡,畫(huà)最小矩形,畫(huà)文字
第一部分:設(shè)置paint
String text = "matt's blog";
int baseLineY = 200;
int baseLineX = 0 ;
//設(shè)置paint
Paint paint = new Paint();
paint.setTextSize(120); //以px為單位
paint.setTextAlign(Paint.Align.LEFT);
設(shè)置paint這部分般贼,主要是設(shè)置字體的大小,因?yàn)槲覀冊(cè)谧煮w所占的區(qū)域大小跟字體的大小是有直接關(guān)系的奥吩,如果不設(shè)置哼蛆,那么在獲取所占區(qū)域大小時(shí),將利用系統(tǒng)默認(rèn)的大小來(lái)測(cè)量了霞赫,當(dāng)然是不行的腮介。
第二部分:畫(huà)text所占的區(qū)域
//畫(huà)text所占的區(qū)域
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
int top = baseLineY + fm.top;
int bottom = baseLineY + fm.bottom;
int width = (int)paint.measureText(text);
Rect rect = new Rect(baseLineX,top,baseLineX+width,bottom);
paint.setColor(Color.GREEN);
canvas.drawRect(rect,paint);
這里就是利用我們前面我們講過(guò)的獲取top線和bottom線的方法,獲取寬度時(shí)就是利用paint.measureText(text)端衰;
然后利用求得到top,bottom,width來(lái)得到對(duì)應(yīng)的矩形:Rect(baseLineX,top,baseLineX+width,bottom)叠洗,這里要注意的是我們利用paint.measureText(text)得到的只是寬度,矩形右下角的x坐標(biāo)值為baselinex+width;
但需要注意的是:矩形右下角的值并不一定是baselinex+width旅东!它的具體取值是跟paint.setTextAlign(Paint.Align.LEFT)有關(guān)的灭抑,因?yàn)槲覀冞@里設(shè)置為Paint.Align.LEFT,所以是baselinex+width抵代。如果設(shè)置為Paint.Align.CENTER,那么右下角的X坐標(biāo)值為baselinex+width/2腾节;再者如果設(shè)置為Paint.Align.RIGHT,那么右下角的X坐標(biāo)就是baselineX;所占矩形的四個(gè)角的所有位置是與paint.setTextAlign()的設(shè)置緊密相關(guān)的,至于各個(gè)點(diǎn)的計(jì)算方法就不再細(xì)講了荤牍,根據(jù)我們前面講的paint.setTextAlign()的顯示效果是非常容易想到的案腺。
第三部分:畫(huà)最小區(qū)域矩形
//畫(huà)最小矩形
Rect minRect = new Rect();
paint.getTextBounds(text,0,text.length(),minRect);
minRect.top = baseLineY + minRect.top;
minRect.bottom = baseLineY + minRect.bottom;
paint.setColor(Color.RED);
canvas.drawRect(minRect,paint);
這部分也就沒(méi)什么難度了,首先根據(jù)paint.getTextBounds()得到基線為y=0的最小矩形的各點(diǎn)坐標(biāo)康吵,然后根據(jù)基線得到其實(shí)際的top和bottom坐標(biāo)劈榨;然后將其畫(huà)出來(lái)即可
第四部分:畫(huà)文字
//畫(huà)文字
paint.setColor(Color.WHITE);
canvas.drawText(text, baseLineX, baseLineY, paint);
四、定點(diǎn)寫(xiě)字
講完上面的三部分晦嵌,這篇文章所要講的知識(shí)點(diǎn)基本就結(jié)束了同辣,下面就是應(yīng)用的部分了,在這部分中耍铜,我們將講述邑闺,當(dāng)我們?cè)O(shè)定一個(gè)點(diǎn),如何到得基線位置棕兼,進(jìn)而畫(huà)出字符串陡舅。
1、給定左上頂點(diǎn)繪圖
這部分伴挚,我們假定給出所要繪制矩形的左上角頂點(diǎn)坐標(biāo)靶衍,然后畫(huà)出這個(gè)文字灾炭。
先來(lái)看效果圖:
在這個(gè)圖中,我們給定左上角的位置颅眶,即(left,top)蜈出;我們知道要畫(huà)文字,drawText()中傳進(jìn)去的Y坐標(biāo)是基線的位置涛酗,所以我們就必須根據(jù)top的位置計(jì)算出baseline的位置铡原。
我們來(lái)看一個(gè)公式:
FontMetrics.top = top - baseline;
所以baseline = top - FontMetrics.top;
因?yàn)镕ontMetrics.top是可以得到的,又因?yàn)槲覀兊膖op坐標(biāo)是給定的商叹,所以通過(guò)這個(gè)公式就能得到baseline的位置了燕刻。
下面舉個(gè)例子來(lái)說(shuō)明一下根據(jù)矩形左上項(xiàng)點(diǎn)繪制文字的過(guò)程:
先看下效果圖:
在這個(gè)效果圖中,因?yàn)槲覀儠?huì)給定矩形左上角頂點(diǎn)(left,top)剖笙,所以們先畫(huà)出top線的位置卵洗,然后計(jì)算出baseline的位置,并畫(huà)出來(lái)弥咪。最后根據(jù)baseline把文字寫(xiě)出來(lái)过蹂。
代碼如下:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text = "matt's blog";
int top = 200;
int baseLineX = 0 ;
//設(shè)置paint
Paint paint = new Paint();
paint.setTextSize(120); //以px為單位
paint.setTextAlign(Paint.Align.LEFT);
//畫(huà)top線
paint.setColor(Color.parseColor("#196F3D"));
canvas.drawLine(baseLineX, top, 2500, top, paint);
//計(jì)算出baseLine位置
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
int baseLineY = top - fm.top;
//畫(huà)基線
paint.setColor(Color.parseColor("#FF0000"));
canvas.drawLine(baseLineX, baseLineY, 2500, baseLineY, paint);
//畫(huà)文字
paint.setColor(Color.BLUE);
canvas.drawText(text, baseLineX, baseLineY, paint);
}
這段代碼,比較簡(jiǎn)單聚至,首先是我們給定top給的位置int top = 200;然后根據(jù)top線位置計(jì)算出baseline所在位置,并畫(huà)出來(lái).
2酷勺、給定中間線位置繪圖
先看效果圖:
在這個(gè)圖中,總共有四條線:top線晚岭,bottom線鸥印,baseline和center線;
圖中center線正是在top線和bottom線的正中間坦报。
為了方便推導(dǎo)公式库说,我另外標(biāo)了三個(gè)距離A,B,C;
很顯然,距離A和距離C是相等的片择,都等于文字所在矩形高度以的一半潜的,即:
A = C = (bottom - top)/2;
又因?yàn)閎ottom = baseline + FontMetrics.bottom;
top = baseline+FontMetrics.top;
所以,將它們兩個(gè)代入上面的公式字管,就可得到:
A = C = (FontMetrics.bottom - FontMetrics.top)/2;
而距離B,則表示Center線到baseline的距離啰挪。
很顯然距離B = C - (bottom - baseline);
又因?yàn)?/p>
FontMetrics.bottom = bottom-baseline;
C = A;
所以,B = A - FontMetrics.bottom;
所以baseline = center + B = center + A - FontMetrics.bottom = center +
(FontMetrics.bottom - FontMetrics.top)/2 - FontMetrics.bottom;
根據(jù)上面的推導(dǎo)過(guò)程嘲叔,我們最終可知亡呵,當(dāng)給定中間線center位置以后,baseline的位置為:
baseline = center + (FontMetrics.bottom - FontMetrics.top)/2 - FontMetrics.bottom;
下面我們舉個(gè)例子來(lái)說(shuō)明下這個(gè)公式的用法硫戈。
效果圖如下:
代碼如下:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text = "matt's blog";
int center = 200;
int baseLineX = 0 ;
//設(shè)置paint
Paint paint = new Paint();
paint.setTextSize(120); //以px為單位
paint.setTextAlign(Paint.Align.LEFT);
//畫(huà)center線
paint.setColor(Color.parseColor("#1A5276"));
paint.setFakeBoldText(true);
paint.setStrokeWidth(3);
canvas.drawLine(baseLineX, center, 3000, center, paint);
//計(jì)算出baseLine位置
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
int baseLineY = center + (fm.bottom - fm.top)/2 - fm.bottom;
//畫(huà)基線
paint.setColor(Color.parseColor("#FF0000"));
paint.setFakeBoldText(true);
paint.setStrokeWidth(3);
canvas.drawLine(baseLineX, baseLineY, 3000, baseLineY, paint);
//畫(huà)文字
paint.setColor(Color.BLUE);
canvas.drawText(text, baseLineX, baseLineY, paint);
}
這段代碼根據(jù)給定中間線的位置為200,然后計(jì)算出baseline的位置锰什,然后把文字在baseline的基礎(chǔ)上畫(huà)出來(lái)。
公眾號(hào):addapp