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而已,其他的操作可以在網上查詢更多的資料自行了解,當然也可以繼續關注我的後續文章。
上圖是我們想要實現的結果,當然看到這個圖的時候,肯定很多人會覺得這樣做其實很簡單,不就是繪製一個圓角的矩形,然後通過計算座標的方式,將文字繪製到矩形的居中位置就好了。好吧,我承認我當時也就是這麼想的,但是在我這麼甘的時候問題就來了。
使用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座標。
本來以為萬無一失的情況,卻想不到來了一記晴天霹靂,這蛋疼的位置是什麼鬼情況。。。。
當然面對這樣的一種情況,還不至於讓我們覺得蛋疼,畢竟調這東西改改算式就好了,慢慢調就好,然!而!事情並沒有那麼簡單,我一開始認為是計算的問題,而後有認為會不會是drawRoundRect和drawText是不是使用的座標不是同一個,為何出現了這樣的偏差呢??但是想想,雖然我大天朝不能用 Google ,但是也不至於這麼蛋疼整我們這些小資程式設計師吧,所以就想了個測試的方法,就是將文字跟的起始座標跟圓角矩形的起始座標設定成一樣,這樣不就能排除問題了嗎?
String text = "購買本章" ;
Paint.FontMetrics fontMetrics = mButtonTextPaint.getFontMetrics();
canvas.drawText(text, startX, startY, mButtonTextPaint);
然後就將繪製文字的程式碼的X,Y座標改成跟RoundRect的X,Y座標一致。
然!而!
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進行重新計算,然後再一次繪製文字。。
我們驚訝的發現,問題就這麼容易就被解決了,而這算式在度娘上也是能夠找到,所以也就不解釋了,到此本文所說的問題也就解決了,希望能夠幫到大家,謝謝!