緣起
自己畫圖在平常開發(fā)中不算很常見的需求凌摄,但偶爾有些需求還必須通過Canvas自己畫出來浪秘,最近筆者就遇到了這樣的事情,由于以前對這些API不是很熟悉丁眼,一路走來也是磕磕絆絆筷凤,不過總算熬過來了,最終也算是對這些API有了更深刻的認(rèn)識苞七,正好寫篇文章記錄下藐守,供參考挪丢。
實際開發(fā)中發(fā)現(xiàn),當(dāng)我們new一個Paint的時候卢厂,其默認(rèn)的style就是FILL模式乾蓬,strokeWidth默認(rèn)大概1像素左右,一般如果你用到的話足淆,最好自己顯式設(shè)置成合適的值巢块,單位為像素。
各種API的使用詳解
Canvas#drawCircle(float cx, float cy, float radius, Paint paint);
作用:畫一個圓巧号;
cx族奢,cy表示圓心,radius表示半徑丹鸿,paint的style如果是stroke則畫出來的是一個鏤空的圓越走,否則會是填充效果的圓;
正常畫圓的時候靠欢,這個API還是很簡單的廊敌,但當(dāng)我們設(shè)置了paint的strokeWidth為某一具體值時,比如50px门怪,即我們想畫一個圓環(huán)效果骡澈。
但當(dāng)你通過這樣的方式畫一個帶寬度的圓環(huán)時,半徑要特別注意下掷空,舉例如下:
假設(shè)你要在寬高都是y的矩形區(qū)域畫一個寬度為strokeW的圓環(huán)肋殴,那么圓環(huán)的半徑應(yīng)該是:
r1 = y /2; 外圓,緊切著最外面的矩形區(qū)域
r2 = y/2 - strokeW; // 半徑稍小的內(nèi)圓
這時坦弟,你可能以為半徑為r2就ok了护锤,可是實際測試發(fā)現(xiàn),當(dāng)通過drawCircle畫一個帶有寬度的圓時(圓環(huán))酿傍,正確的半徑應(yīng)該是r = r2 + strokeW/2, 比想象中的要往外再擴大點烙懦,即要加上一半的strokeW;
可以簡單理解成有寬度時,需要的半徑是中心圓(即不是內(nèi)圓也不是外圓)的半徑(外圓半徑-strokeW/2或者內(nèi)圓半徑+strokeW/2)赤炒。Canvas#drawOval(RectF oval, Paint paint);
作用:畫橢圓
oval:給定的矩形區(qū)域氯析,在這個邊界內(nèi)畫橢圓(矩形的內(nèi)切圓),如果這個矩形區(qū)域恰好是正方形可霎,那么畫出來的橢圓實際上就是圓了魄鸦,介紹這個主要是為了給下面的畫弧線做鋪墊。Canvas#drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint);
作用:畫弧線癣朗,橢圓的某一部分拾因;
oval: 弧線所在的橢圓的邊界矩形區(qū)域,同上面的解釋;
startAngle: 起始角度绢记,0度是指x軸正方向(鐘表上的3點鐘方向)扁达,正數(shù)表示沿順時針方向轉(zhuǎn),負(fù)數(shù)則是逆時針方向蠢熄,<0或>360實際相當(dāng)于startAngle%360;
sweepAngle: 掃過的角度跪解,正數(shù)表示順時針掃過,負(fù)數(shù)表示逆時針掃過签孔,如果>360 or <-360叉讥,則整個橢圓會被畫出來;
useCenter:一般為false饥追,不包括中心點图仓,可以設(shè)為true來畫一部分扇形;Canvas#drawText(String text, float x, float y, Paint paint)但绕;
作用:畫文字救崔,這個時候經(jīng)常需要調(diào)用paint#setTextSize方法來設(shè)置文字的大小,單位是px捏顺;
其中Paint里面的textAlign六孵,對最終結(jié)果會有重要的影響。
paint.setTextAlign(Paint.Align.RIGHT);
比如text是“你好啊世界”
假設(shè)你Text align是Left幅骄,那么 x,y是相對text的左邊說的劫窒,也就是會把“你”這個文字畫在x的位置;
center也是同樣的道理拆座,x表示中間字的位置烛亦,Right是說會把“界”字畫在x位置,其他文字還是會在左邊依次畫出來懂拾;
這里的y是指文字的baseline,并不是我們常見的top頂部铐达,效果上是要偏下方點岖赋,注意這個區(qū)別。Canvas#transalte(dx, dy) 平移畫布
作用:將當(dāng)前的畫布坐標(biāo)原點移動dx瓮孙,dy唐断,比如原先是0,0杭抠,經(jīng)過(100, 100)操作后脸甘,新的繪制對應(yīng)的原點就是(100,100)偏灿;Canvas#scale(sx, sy)丹诀、scale(sx, sy, px, py) 縮放畫布
這2個方法的區(qū)別,可以看下源碼,很清楚的展現(xiàn)了區(qū)別:
public void scale(float sx, float sy) {
native_scale(mNativeCanvasWrapper, sx, sy);
}
public final void scale(float sx, float sy, float px, float py) {
translate(px, py);
scale(sx, sy);
translate(-px, -py);
}
畫布的縮放铆遭,比如scale(0.5f, 0.5f) 表示x硝桩、y方向上各縮小一半,比如畫了個400*400的矩形枚荣,出來的效果就是200*200的碗脊,px,py表示對原點的平移橄妆。
-
Canvas#rotate(float degrees, float px, float py); 旋轉(zhuǎn)畫布
它的理解幾乎和scale一模一樣衙伶,這個方法在有些情況下是非常有用的,比如有個需求是要畫個類似鐘表刻度那樣的界面害碾,如下:
鐘表刻度盤
你要做的只是畫一條垂直方向的直線(線段)矢劲,然后不停的旋轉(zhuǎn)不同的角度即可(旋轉(zhuǎn)360度),按照這個思路一下子就可以將這個復(fù)雜的問題完美解決掉蛮原。
對這幾個變換方法的理解可以參考下這篇文章卧须,寫的非常不錯。 給Paint設(shè)置漸變器Shader儒陨;
對Shader的理解可以參考這篇文章Shader圖文詳解 花嘶。
經(jīng)常我們需要用漸變色填充一個區(qū)域(比如由path決定的),實際中常用在繪制收益率曲線蹦漠,然后將曲線圍起來的下方區(qū)域用漸變色填充起來椭员,示意代碼如下:
private void fillRegionWithGradientColor(Canvas canvas, Paint paint) {
int w = getWidth();
int h = getHeight();
Path path1 = new Path();
path1.moveTo(0, h);
path1.lineTo(100, 200);
path1.lineTo(150, 300);
path1.lineTo(w/2, h/2);
path1.lineTo(w/2+150, h/2);
path1.lineTo(w, h);
//path1.close();
// 為Paint設(shè)置漸變器 在豎直方向從h/2到h漸變2個顏色值
Shader mShader = new LinearGradient(0, h/2, 0, h, new int[] {
0xffff5377, 0xfffeeeee}, null,
Shader.TileMode.CLAMP);
paint.setShader(mShader);
canvas.drawPath(path1, paint);
}
- Paint#setShadowLayer(float radius, float dx, float dy, int shadowColor);
作用:實現(xiàn)在幾何圖形底部畫陰影的效果,可以想象成陽光從上往下斜著照射笛园;
radius:陰影模糊層的高度隘击,為0表示沒有陰影層,值越大陰影區(qū)域越大但也更模糊研铆,人視覺上的效果相當(dāng)于在dx/dy偏移量的基礎(chǔ)上再拼上個radius這么大的模糊層埋同,一般你不想讓它看起來很模糊的話,傳一個比較小的值棵红,比如傳1就好了凶赁,注意當(dāng)這個值為0時,即使有dx/dy也不會有陰影畫出來逆甜;
dx虱肄,dy:這2個值是陰影圖形相對于原圖形的偏移,可以想象成陽光從上往下以不同的角度照過來交煞,比如都為正數(shù)30咏窿,相當(dāng)于往右下角平移30個像素;
shadowColor:陰影的顏色素征,如果這個顏色值不帶透明度的話集嵌,那透明度會使用Paint的(如果它有的話)萝挤,否則用陰影自己的透明度;
總結(jié)
這些API其實用起來也沒多少難度纸淮,大家在不確定的時候最好能夠動手寫個小demo跑起來看看效果平斩,一般也都能搞明白作用。