1. 程式人生 > >Canvas使用 -- 在canvas上繪製圓角矩形並新增文字

Canvas使用 -- 在canvas上繪製圓角矩形並新增文字

前言

先來閒聊一下寫這個題目的原因吧,其實這個知識點對於大神來說其實是很簡單的,所以如果大神看到這裡的話,其實就可以不用看下去了。至於如果是新手,或者說跟本人一樣,對於canvas的操作還有疑惑的朋友,希望你可以從這篇文章中得到你想要的答案,同時可以解決你的問題。
其實這個文章源自於最近專案需要重構,不得不說,作為一個邁過十年時光的專案來說,要進行重構還是比較煩惱的,至於這個煩惱的原因吧,不用多說,大家也應該知道在產品X閒著沒事,正在為自己的KPI而擔心的時候,總想搞點事情,這也是無可厚非,尤其是一個菜鳥產品X急切需要表現的時候。當然了,話雖這麼說,但是能夠為這個有十年光景的專案進行一次重構,心裡還是有點小激動的,畢竟能夠吸收前人的思想,同時自己又可以在專案中大展身手,對自己來說也是好的。
那麼我就來說說這個需求吧,因為我們應用是小說閱讀器,所以自然閱讀頁上面肯定是重中之重,而這一塊在效能上的要求無疑也是比較高的,如果單純使用各種View的組合成為一個新的View,明顯這樣也是可以的,但是這樣的效能必然不是很好,因為這裡涉及到多層View的巢狀操作,同時對檢視的過度渲染也會導致卡慢等的情況出現,所以最好的方式我們最好還是通過在canvas進行操作了。當然這裡我給出的只是canvas操作中的一個我遇到問題時的操作,這些問題可能在大牛看來不值一提,但是哪個敢說未來的大牛不會踩一下坑呢?所以我就只能給自己這樣的小白歸納一下自己的問題了,當然由於是重構專案,後續肯定還會有各種各樣的問題,所以如果有興趣的朋友可以關注我的部落格,後續我會將我重構中的問題一一收集。

正文

首先我們先來看看canvas的一些基本的常用的操作

操作型別 相關API 備註
繪製顏色 drawColor
drawRGB
drawARGB
使用單一顏色填充canvas
繪製基本形狀 drawPoint
drawPoints
drawLine
drawLines
drawRect
drawRoundRect
drawOval,drawCircle,drawArc
依次是點、線、矩形、圓角矩形
橢圓、圓、圓弧
繪製圖片 drawBitmap,drawPicture 繪製點陣圖和圖片
繪製文字 drawText,drawPostText
drawTextOnPath
依次是繪製文字、繪製文字時根據制定每個文字位置、
根據路徑繪製文字
繪製路徑 drawPath 繪製路徑,繪製貝塞爾曲線
時也需要用到該函式
頂點操作 drawVertices
drawBitmapMesh
通過對頂點操作可以使影象形變,
drawVertices直接對畫布作用、
drawBitmapMesh只對繪製的bitmap作用
畫布裁剪 clipPath,clipRect 設定畫布的顯示區域
畫布快照 save,restore,
saveLayerXxx,
restoreToCount,
getSaveCount
依次是儲存當前狀態、回滾到上一次儲存的狀態、
儲存圖層狀態、回滾到制定狀態、
獲取儲存次數
畫布變換 translate,scale,
rotate,skew
依次是位移、縮放、旋轉、錯切
Matrix矩陣 getMatrix,setMatrix,
concat
實際上畫布的位移,縮放等操作的都是影象
矩陣Matrix,只不過Matrix比較難以理解
故封裝了一些常用的方法



當然這麼多的操作,其實都只是列舉,實際上這篇文章中,我們用到的僅僅只是drawRoundRect以及drawText而已,其他的操作可以在網上查詢更多的資料自行了解,當然也可以繼續關注我的後續文章。

NeoBosco

上圖是我們想要實現的結果,當然看到這個圖的時候,肯定很多人會覺得這樣做其實很簡單,不就是繪製一個圓角的矩形,然後通過計算座標的方式,將文字繪製到矩形的居中位置就好了。好吧,我承認我當時也就是這麼想的,但是在我這麼甘的時候問題就來了。

使用drawRoundRect()和drawText()繪製時總會出現莫名其妙的偏差

float startX = 100 ;
float startY = 100 ;
float endX = 600 ;
float endY = 200 ;
RectF rectF = new RectF(startX, startY, endX, endY);
canvas.drawRoundRect(rectF, 20, 20, mButtonPaint);

首先我們通過上面程式碼在canvas上繪製出一個圓角的矩形。說到這裡就要提一句,canvas.drawRoundRect()有兩個不同參的函式,因為博主的專案中需要相容較低的android系統版本,所以採用上面比較通用的繪製圓角矩形的API。當然這裡可能還有一些讀者會問Rect和RectF的區別,這個其實度娘一下就已經有很多答案了,博主就不詳述了,它代表了座標系內某一塊矩形區域的引數封裝,而Rect跟RectF區別在於得到的值的精度問題以及部分API的不同。

String text = "購買本章" ;
Paint.FontMetrics fontMetrics = mButtonTextPaint.getFontMetrics();
canvas.drawText(text, (endX - startX) / 2 + startX , (fontMetrics.bottom - fontMetrics.top) / 2 + startY , mButtonTextPaint);

接著通過上面的程式碼,在圓角矩形的居中的位置繪製文字,這裡的計算比較簡單,就是通過畫筆Paint.FontMetrics對文字進行計算。
繪製的文字的X座標 = (結束的X座標 - 起始的X 座標) / 2 + 起始座標。
繪製文字的Y座標 = (文字的bottom座標 - 文字的top座標) / 2 + 起始的Y座標。

NeoBosco

本來以為萬無一失的情況,卻想不到來了一記晴天霹靂,這蛋疼的位置是什麼鬼情況。。。。
當然面對這樣的一種情況,還不至於讓我們覺得蛋疼,畢竟調這東西改改算式就好了,慢慢調就好,然!而!事情並沒有那麼簡單,我一開始認為是計算的問題,而後有認為會不會是drawRoundRectdrawText是不是使用的座標不是同一個,為何出現了這樣的偏差呢??但是想想,雖然我大天朝不能用 Google ,但是也不至於這麼蛋疼整我們這些小資程式設計師吧,所以就想了個測試的方法,就是將文字跟的起始座標跟圓角矩形的起始座標設定成一樣,這樣不就能排除問題了嗎?

String text = "購買本章" ;
Paint.FontMetrics fontMetrics = mButtonTextPaint.getFontMetrics();
canvas.drawText(text, startX, startY, mButtonTextPaint);

然後就將繪製文字的程式碼的X,Y座標改成跟RoundRect的X,Y座標一致。
然!而!
NeoBosco

WTF~~這是什麼鬼。。為什麼即使設定成一樣的X,Y座標卻相差那麼遠,這不是瞎搞嗎?來到這個地步,難道還敢說這坑爹的不是兩種不同的座標系?
然!而!峰迴路轉的是,這還真不是兩個座標系不同,這裡面還有一個坑爹的知識點,就是baseline的概念,什麼是baseline?就是所謂的基線了,至於概念這裡就長篇大論了,這個在度娘上面,你找到的解釋簡直是多如繁星。接著,我們知道問題可能在文字的baseline上面的話,我們就可以嘗試一下這個問題了。

Paint.FontMetrics fontMetrics = mButtonTextPaint.getFontMetrics();
float baseline = (rectF.bottom + rectF.top - fontMetrics.bottom - fontMetrics.top) / 2;
canvas.drawText(text, rectF.centerX(), baseline, mButtonTextPaint);

所以這裡我就將baseline進行重新計算,然後再一次繪製文字。。

NeoBosco

我們驚訝的發現,問題就這麼容易就被解決了,而這算式在度娘上也是能夠找到,所以也就不解釋了,到此本文所說的問題也就解決了,希望能夠幫到大家,謝謝!