android自定義View:drawText詳解

一朋沮、概述

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市汁胆,隨后出現(xiàn)的幾起案子梭姓,更是在濱河造成了極大的恐慌,老刑警劉巖嫩码,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件誉尖,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡铸题,警方通過(guò)查閱死者的電腦和手機(jī)铡恕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)回挽,“玉大人没咙,你說(shuō)我怎么就攤上這事∏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵牌捷,是天一觀的道長(zhǎng)墙牌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)暗甥,這世上最難降的妖魔是什么喜滨? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮撤防,結(jié)果婚禮上虽风,老公的妹妹穿的比我還像新娘。我一直安慰自己寄月,他們只是感情好辜膝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著漾肮,像睡著了一般厂抖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上克懊,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天忱辅,我揣著相機(jī)與錄音,去河邊找鬼谭溉。 笑死墙懂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扮念。 我是一名探鬼主播损搬,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了场躯?” 一聲冷哼從身側(cè)響起谈为,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎踢关,沒(méi)想到半個(gè)月后伞鲫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡签舞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年秕脓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片儒搭。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吠架,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搂鲫,到底是詐尸還是另有隱情傍药,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布魂仍,位于F島的核電站拐辽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏擦酌。R本人自食惡果不足惜俱诸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赊舶。 院中可真熱鬧睁搭,春花似錦、人聲如沸笼平。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)出吹。三九已至遇伞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間捶牢,已是汗流浹背鸠珠。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秋麸,地道東北人渐排。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像灸蟆,于是被迫代替她去往敵國(guó)和親驯耻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容