Android自定義View工具:Paint&Canvas(二)

_小馬快跑_發表於2017-12-15

接上篇,Android自定義View工具:Paint&Canvas(一),上篇主要介紹的是Paint,本篇接著來看Canvas。

Canvas常用方法一覽:

Canvas常用方法 備註
繪製顏色
drawARGB 通過設定ARGB值繪製顏色
drawRGB 通過設定RGB值繪製顏色
drawColor 繪製顏色
繪製圖形
drawPoint,drawPoints 繪製點,點集合
drawLine,drawLines 繪製線,線集合
drawRect 繪製矩形
drawCircle 繪製圓
drawOval 繪製橢圓
drawArc 繪製弧
畫布操作
translate、rotate、scale、save、restore 依次為位移、旋轉、縮放、儲存畫布和恢復畫布
drawPath 按路徑繪製
  • ####Canvas繪製顏色:
canvas.drawARGB(int a, int r, int g, int b)
canvas.drawRGB(int r, int g, int b)
canvas.drawColor(int color) 
canvas.drawColor(int color, PorterDuff.Mode mode)
複製程式碼

顏色的四種模式:

顏色模式 備註
ARGB8888 四通道高精度(32位)
ARGB4444 四通道低精度(16位)
RGB565 螢幕預設模式(16位)
Alpha8 僅有透明通道(8位)

ARGB:

型別 備註
A(Alpha) 透明度,取值範圍 [0,255],0代表完全透明,255代表完全不透明
R(Red) 紅色,取值範圍 [0,255],0代表無色,255代表紅色
G(Green) 綠色,取值範圍 [0,255],0代表無色,255代表綠色
B(Blue) 藍色,取值範圍 [0,255],0代表無色,255代表藍色

其中ARGB的取值範圍均為0~255(即16進位制的0x00~0xff) A 從0x00到0xff表示從透明到不透明。 RGB 從0x00到0xff表示顏色從淺到深。 當RGB全取最小值(0或0x000000)時顏色為黑色,全取最大值(255或0xffffff)時顏色為白色 示例程式碼:

canvas.drawARGB(255, 0, 0, 0);
canvas.drawARGB(255, 255, 0, 0);
canvas.drawARGB(255, 0, 255, 0);
canvas.drawARGB(255, 0, 0, 255);
複製程式碼

效果圖分別為:

ARGB.png

  • ####Canvas繪製形狀:
canvas.drawPoint(float x, float y, Paint paint) //繪製點
canvas.drawPoints( float[] pts, Paint paint) //繪製多個點

canvas.drawLine(float startX, float startY, float stopX, float stopY, Paint paint) //繪製線
canvas.drawLines(float[] pts, Paint paint)//繪製多條線

canvas.drawRect(float left, float top, float right, float bottom, Paint paint) //繪製矩形
canvas.drawRect(RectF rect, Paint paint) //繪製矩形

canvas.drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,Paint paint) //繪製圓角矩形
canvas.drawRoundRect(RectF rect, float rx, float ry, Paint paint) //繪製圓角矩形

canvas.drawCircle(float cx, float cy, float radius,Paint paint) 繪製圓

canvas.drawOval(float left, float top, float right, float bottom,Paint paint) //繪製橢圓
canvas.drawOval(RectF oval,Paint paint) //繪製橢圓

canvas.drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter,Paint paint) //繪製圓弧
canvas.drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint) ////繪製圓弧
複製程式碼

1. 畫點:

Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(15f);
canvas.drawPoint(400, 400, mPaint);
float[] pts = {500, 500, 500, 600};
canvas.drawPoints(pts, mPaint);
複製程式碼

drawPoint前兩個引數為點的座標(x,y),而drawPoints方法第一個引數是float[]陣列,每兩個代表一個座標,如上面{500, 500, 500, 600}代表兩個座標點(500,500)和(500,600),效果圖:

point.png
2. 畫線:

Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(15f);
canvas.drawLine(200, 500, 800, 500, mPaint);
float[] pts = {200, 200, 500, 500,
               800, 200, 500, 500};
canvas.drawLines(pts, mPaint);
複製程式碼

drawLine前四個引數代表起始座標(x1,y1)和終點座標(x2,y2),而drawLines第一個引數是float[]陣列,每四個代表一組起始座標和終點座標的組合,如上圖{200, 200, 500, 500,800, 200, 500, 500}代表兩組座標點:起始座標(200,200)終點座標(500, 500),以及起始座標(800, 200)和終點座標(500, 500),效果圖:

line.png

3. 畫矩形:

Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10f);
//1.直接通過座標畫矩形
canvas.drawRect(200, 200, 1000, 600, mPaint);
//2.通過Rect 畫矩形
Rect mRect = new Rect(200, 650, 1000, 1050);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawRect(mRect, mPaint);
//3.通過RectF 畫矩形
RectF mRectF = new RectF(200, 1100, 1000, 1500);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10f);
canvas.drawRect(mRectF, mPaint);
複製程式碼

通過獲得四個點的值,即左上角(x1,y1)和右下角(x2,y2)的座標來確定矩形,效果圖:

rectangle.png
4. 畫圓:

drawCircle(float cx, float cy, float radius, Paint paint)
複製程式碼

(cx,cy)是圓點座標,radius是圓半徑,示例:

Paint mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10F);
mPaint.setColor(Color.RED);
canvas.drawCircle(600, 600, 200, mPaint);
複製程式碼

效果圖:

circle.png
5. 畫橢圓:

drawOval( RectF oval,Paint paint)
drawOval(float left, float top, float right, float bottom, Paint paint)
複製程式碼

第一個方法看原始碼還是呼叫的第二個方法,示例:

Paint mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLUE);
mPaint.setStrokeWidth(10f);
//第一種,通過left top right bottom來確定矩形,然後畫矩形的內切橢圓
canvas.drawOval(300, 100, 900, 500, mPaint);
RectF mRectF = new RectF(100, 600, 1000, 900);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
//第二種,通過RectF 畫橢圓,和第一種原理是一樣的
canvas.drawOval(mRectF, mPaint);
複製程式碼

效果圖:

oval.png
6. 畫圓角矩形:

//第一個方法必須SDK>=21以上
drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)
drawRoundRect(RectF rect, float rx, float ry, Paint paint)
複製程式碼

示例:

float rRadius=20f;
Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10f);
//第一種 必須SDK>=21以上使用
canvas.drawRoundRect(200, 100, 1000, 500, rRadius, rRadius, mPaint);
//第二種通過RectF 來畫圓角矩形
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
RectF mRect = new RectF(200, 600, 1000, 1000);
canvas.drawRoundRect(mRect, rRadius, rRadius, mPaint);
複製程式碼

RectF建構函式中的四個引數分別為上,左,右,下座標,即左上角和右下角座標,rx,ry表示的圓弧半徑(橢圓圓弧),效果圖:

roundrectangle.png
7. 畫弧:

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint)
drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter, Paint paint)
複製程式碼

第一個方法預設也會去呼叫第二個方法,看第二個方法:

drawArc 備註
left 左上角座標的X軸座標
top 左上角座標的Y軸座標
right 右下角座標X軸座標
bottom 右下角座標Y軸座標
startAngle 起始角度
sweepAngle 掃過的角度
useCenter 是否使用中心
paint 畫筆

示例:

Paint mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(10f);
//通過下面前兩個圓弧對比很明顯,useCenter為true,最後圖形是一個扇形,
//useCenter為false,則最後圖形是起始點和結束點之間的連線和圓弧圍成的面積。
canvas.drawArc(100, 100, 500, 500, 0, 90, true, mPaint);
canvas.drawArc(100, 500, 500, 900, 0, 90, false, mPaint);
//通過RectF 設定的區域範圍畫弧
RectF mRectF = new RectF(100, 900, 500, 1300);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawArc(mRectF, 0, 90, true, mPaint);
複製程式碼

效果圖:

arc.png

  • ####Canvas畫布操作:
canvas.translate(float dx, float dy) //平移
canvas.rotate(float degrees) //旋轉
canvas.rotate(float degrees, float px, float py) //改變旋轉中心並旋轉
canvas.scale(float sx, float sy) //縮放
canvas.scale(float sx, float sy, float px, float py) //改變縮放中心並縮放
canvas.save(); //儲存畫布
canvas.restore(); //恢復畫布
複製程式碼

Canvas畫布操作可以讓我們用更容易的方式來繪製圖形,Canvas畫布操作只會對後面的繪製起作用,對前面已經繪製的不起作用。 1. translate translate是對座標系的平移,且是可以多次疊加的,預設是在螢幕的左上角(0,0),示例:

Paint mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
//將座標系移到Canvas寬度的一半,高度400的位置
canvas.translate(getMeasuredWidth() / 2, 400);
//繪製一個紅色圓
mPaint.setColor(Color.RED);
canvas.drawCircle(0, 0, 100, mPaint);
//座標系原點在前面位置的基礎上再往下移動200畫素
canvas.translate(0, 200);
//繪製一個黃色圓
mPaint.setColor(Color.YELLOW);
canvas.drawCircle(0, 0, 100, mPaint);
//座標系原點在前面位置的基礎上再往下移動200畫素
canvas.translate(0, 200);
//繪製一個綠色圓
mPaint.setColor(Color.GREEN);
canvas.drawCircle(0, 0, 100, mPaint);
複製程式碼

效果圖:

translate.png

2. rotate rotate是對座標系的旋轉,rotate有兩個方法:

canvas.rotate(float degrees) 
canvas.rotate(float degrees, float px, float py) 
複製程式碼

第二個方法中的(px,py)可以移動旋轉中心然後旋轉,示例:

Paint mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
//將座標系移動到螢幕中心
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
RectF rectF = new RectF(-300, -300, 0, 0);
//繪製一個紅色矩形
mPaint.setColor(Color.RED);
canvas.drawRect(rectF, mPaint);
//將座標系旋轉180度,不會影響前面已經繪製的圖形
canvas.rotate(180);
mPaint.setStyle(Paint.Style.FILL);
//繪製一個綠色矩形
mPaint.setColor(Color.GREEN);
canvas.drawRect(rectF, mPaint);
複製程式碼

效果圖:

rotate.png
上面程式碼如果改變旋轉中心再旋轉:

//將座標系中心左移(-150,0),並旋轉180度
canvas.rotate(180,-150,0);
複製程式碼

效果圖:

rotateCenter.png

可以旋轉中心向左移了150畫素,而原來的旋轉中心已經不再是(0,0)了,而成了(150,0),旋轉中心變了,旋轉後繪製的圖形位置也就不一樣了。

3. scale

canvas.scale(float sx, float sy)
canvas.scale(float sx, float sy, float px, float py)
複製程式碼

scale是縮放,縮放中心預設是座標原點,且多次縮放是可以疊乘的,和rotate一樣有2個方法,其中多出來的兩個引數類似rotate的旋轉中心,px,py是控制縮放中心位置,sx,sy是X軸和Y軸的縮放倍數,示例:

Paint mPaint = new Paint();
//座標原點移到螢幕中心
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
RectF rectF = new RectF(-300, -300, 0, 0);
mPaint.setColor(Color.RED);
//左上角繪製紅色圓
canvas.drawRect(rectF, mPaint);
//X軸 Y軸分別縮放到原來的1/2並以原點(0,0)位對稱點進行翻轉
canvas.scale(-0.5f, -0.5f);
//繪製綠色的圓
mPaint.setColor(Color.GREEN);
canvas.drawRect(rectF, mPaint);
複製程式碼

效果圖:

scale1.png
修改上面scale方法:

canvas.scale(-0.5f, -0.5f, 100, 0)
複製程式碼

效果圖:

scale2.png
縮放中心向右移動100畫素,X軸 Y軸分別縮放到原來的1/2,並且以新的縮放中心為對稱點進行翻轉,在修改一下scale引數:

canvas.scale(0.5f, 0.5f);
canvas.scale(-0.5f, 0.5f);
canvas.scale(0.5f, -0.5f);
複製程式碼

效果圖分別為:

initpintu.jpg
總結一下sx sy

sx sy取值範圍 備註
(1,+∞) 根據縮放中心放大到原來的n倍
1 跟原來大小一樣,沒變化
(0,1) 根據縮放中心縮放
0 sx sy有一個取0圖形就消失了
(-1,0) 根據縮放中心縮放並翻轉
-1 翻轉
(-∞,-1) 根據縮放中心放大並翻轉

4. save、restore:

save:儲存之前Canvas的狀態,save之後,可以呼叫Canvas的平移、縮放、旋轉、錯切、裁剪等操作。 restore:恢復Canvas之前儲存的狀態,防止save後對Canvas執行的操作對後續的繪製有影響。 canvas的save 和 restore是成對使用(restore只能比save少,不能多),示例:

Paint paint = new Paint();
//儲存畫布
canvas.save();
//座標原點移到螢幕中心
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
//以螢幕中心為座標原點在(60,50)為圓心處繪製紅色圓
paint.setColor(Color.RED);
canvas.drawCircle(60, 50, 100, paint);
//恢復畫布
canvas.restore();
//恢復畫布後,座標原點(0,0)預設在螢幕左上角,
//即以螢幕左上角為座標原點在(60,50)為圓心處繪製黑色圓
paint.setColor(Color.BLACK);
canvas.drawCircle(60, 50, 50, paint);
複製程式碼

效果圖:

儲存恢復畫布.png

上面程式碼中如果去掉save和restore過程,即:

Paint paint = new Paint();
//canvas.save();
//座標原點移到螢幕中心
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
//以螢幕中心為座標原點在(60,50)為圓心處繪製紅色圓
paint.setColor(Color.RED);
canvas.drawCircle(60, 50, 100, paint);
//canvas.restore();
//沒有了save和restore操作,座標原點沒有恢復到螢幕左上角,還是在螢幕中心
//即以螢幕中心為座標原點在(60,50)為圓心處繪製黑色圓
paint.setColor(Color.BLACK);canvas.drawCircle(60, 50, 50, paint);
複製程式碼

效果圖是:

nosave.png

為什麼效果不一樣呢?這是因為如果去掉save和restore過程,所有影像都是在座標原點移動到螢幕中心後繪製的;如果有save和restore,那麼在restore之後進行的影像繪製原點又回到了螢幕左上角,可見save和restore呼叫時機會影響到最後繪圖結果,save和restore之間往往是進行一系列的Canvas操作。

  • drawPath
canvas.drawPath( Path path, Paint paint)
複製程式碼

drawPath內容較多,見下篇:Android Canvas之Path操作

相關文章