先來閑聊一下寫這個題目的原因吧,其實這個知識點對于大神來說其實是很簡單的,所以如果大神看到這里的話,其實就可以不用看下去了。至于如果是新手,或者說跟本人一樣,對于canvas的操作還有疑惑的朋友,希望你可以從這篇文章中得到你想要的答案,同時可以解決你的問題。 其實這個文章源自于最近項目需要重構,不得不說,作為一個邁過十年時光的項目來說,要進行重構還是比較煩惱的,至于這個煩惱的原因吧,不用多說,大家也應該知道在產品X閑著沒事,正在為自己的KPI而擔心的時候,總想搞點事情,這也是無可厚非,尤其是一個菜鳥產品X急切需要表現的時候。當然了,話雖這么說,但是能夠為這個有十年光景的項目進行一次重構,心里還是有點小激動的,畢竟能夠吸收前人的思想,同時自己又可以在項目中大展身手,對自己來說也是好的。 那么我就來說說這個需求吧,因為我們應用是小說閱讀器,所以自然閱讀頁上面肯定是重中之重,而這一塊在性能上的要求無疑也是比較高的,如果單純使用各種View的組合成為一個新的View,明顯這樣也是可以的,但是這樣的性能必然不是很好,因為這里涉及到多層View的嵌套操作,同時對視圖的過度渲染也會導致卡慢等的情況出現,所以最好的方式我們最好還是通過在canvas進行操作了。當然這里我給出的只是canvas操作中的一個我遇到問題時的操作,這些問題可能在大牛看來不值一提,但是哪個敢說未來的大牛不會踩一下坑呢?所以我就只能給自己這樣的小白歸納一下自己的問題了,當然由于是重構項目,后續肯定還會有各種各樣的問題,所以如果有興趣的朋友可以關注我的博客,后續我會將我重構中的問題一一收集。
首先我們先來看看canvas的一些基本的常用的操作
| 操作類型 | 相關API | 備注 |
|---|---|---|
| 繪制顏色 | drawColordrawRGBdrawARGB | 使用單一顏色填充canvas |
| 繪制基本形狀 | drawPointdrawPointsdrawLinedrawLinesdrawRectdrawRoundRectdrawOval,drawCircle,drawArc | 依次是點、線、矩形、圓角矩形橢圓、圓、圓弧 |
| 繪制圖片 | drawBitmap,drawPicture | 繪制位圖和圖片 |
| 繪制文本 | drawText,drawPostTextdrawTextOnPath | 依次是繪制文字、繪制文字時根據制定每個文字位置、根據路徑繪制文字 |
| 繪制路徑 | drawPath | 繪制路徑,繪制貝塞爾曲線時也需要用到該函數 |
| 頂點操作 | drawVerticesdrawBitmapMesh | 通過對頂點操作可以使圖像形變,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進行重新計算,然后再一次繪制文本。。

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