前言:當我們進行自定義控件的時候,為了實現更多更炫酷的效果,我們通常都需要在Canvas(畫布)
上繪制各種東西。而谷歌也提供了很多的API方法,能讓我們更方便更快捷的實現想要的效果。
Canvas
public class Canvas
extends Object
java.lang.Object
↳ android.graphics.Canvas
Canvas
類包含“繪制”調用。要繪制一些東西,你需要4個基本組件:一個用於保存像素的位圖,一個用於托管繪圖調用的Canvas(寫入位圖),一個繪圖基元(例如Rect,Path,text,Bitmap)和一個paint(描述繪圖的顏色和樣式)。
下面介紹下Canvas 的常用方法,先看下總的內容:
一、畫布變換
1、translate(位移)
/** * 畫布向(100,100)方向平移 * * @param dx 向X軸方向平移100px * @param dy 向Y軸方向平移100px */
canvas.translate(100, 100);
看下效果圖:
注意
:translate()是可以疊加的,而且這里實際上移動的是坐標,畫布的位置並沒有變,也就是說坐標原點變到了原來(100,100)的位置
2、rotate(旋轉)
/** * 原點為中心,旋轉45度(順時針方向為正方向 ) * * @param degrees 旋轉角度 */
canvas.rotate(45);
效果圖如下:
旋轉還有另外一個方法:canvas.rotate(float degrees, float px, float py);
/** * Preconcat the current matrix with the specified rotation. * * @param degrees The amount to rotate, in degrees * @param px The x-coord for the pivot point (unchanged by the rotation) * @param py The y-coord for the pivot point (unchanged by the rotation) */
public final void rotate(float degrees, float px, float py) {
if (degrees == 0.0f) return;
translate(px, py);
rotate(degrees);
translate(-px, -py);
}
根據源碼可以很明顯的看到,其實就是先平移到指定位置,在指定位置進行了旋轉之后又對畫布做了返回平移處理,這樣就實現了以自定義點為中心旋轉畫布
的效果。
3、scale(縮放)
/** * 在X軸方向放大為原來2倍,Y軸方向方大為原來的4倍 * * @param sx X軸的放大倍數 * @param sy Y軸的放大倍數 */
canvas.scale(2, 4);
效果圖如下:
縮放的另外一個方法:canvas.scale(float sx, float sy, float px, float py);
/** * Preconcat the current matrix with the specified scale. * * @param sx The amount to scale in X * @param sy The amount to scale in Y * @param px The x-coord for the pivot point (unchanged by the scale) * @param py The y-coord for the pivot point (unchanged by the scale) */
public final void scale(float sx, float sy, float px, float py) {
if (sx == 1.0f && sy == 1.0f) return;
translate(px, py);
scale(sx, sy);
translate(-px, -py);
}
同理,也是先實現平移,在指定位置縮放之后又對畫布做了返回平移處理,從而實現了以自定義點為中心縮放畫布
的效果。
4、skew(傾斜)
/** * 在Y軸方向傾斜 * * @param sx x軸方向上傾斜的對應角度 * @param sy Y軸方向上傾斜的對應角度 */
canvas.skew(0,1f);
注意
:這里的傾斜的角度都是tan值,比如要在x軸方向上傾斜60度,那么小數值對應:tan 60 = 根號3 = 1.732!因為效果不好畫,就不上效果圖了,畢竟這個方法用的也少。
二、繪制方法(drawXXX)
1、drawPoint、drawPoints(繪制點)
/** * 繪制出一個點 * * @param x X軸方向上的位置 * @param y Y軸方向上的位置 * @param Paint 畫筆對象 */
canvas.drawPoint(200, 200, redPaint);
點的大小取決於畫筆的大小,不光可以繪制出單個點,也可以繪制出多個點:
float pts[] = {200, 200, 250, 250, 300, 300, 350, 350};
/** * 繪制出多個點 * * @param pts 要繪制點的float數組,格式是[x0 y0 x1 y1 x2 y2 ...],每兩組一個點,最后不足兩個的,忽略最后的值 * @param Paint 畫筆對象 */
canvas.drawPoints(pts, redPaint);
看效果圖:
2、drawLine、drawLines(繪制線)
/** * 繪制出一條線 * * @param startX 線的起點的X坐標 * @param startY 線的起點的Y坐標 * @param stopX 線的終點的X坐標 * @param stopY 線的終點的X坐標 * @param Paint 畫筆對象 */
canvas.drawLine(200, 200, 400, 400, redPaint);
線的大小也取決於畫筆的大小,並且也是可以繪制出多條線:
float[] pts = new float[]{100, 100, 200, 200, 200, 100, 300, 100};
/** * 同時繪制多條線。 * * @param pts 要繪制線的float數組,格式是每四個一組為一條線。最后不足四個,就忽略那些值。 * @param Paint 畫筆對象 */
canvas.drawLines(pts, redPaint);
看一下效果圖:
3、drawRect、drawRoundRect(繪制矩形)
/** * 矩形 (第一種方法) * * @param left int 矩形左側的X坐標 * @param top int 矩形頂部的Y坐標 * @param right int 矩形右側的X坐標 * @param bottom int 矩形底部的Y坐標 */
Rect rect = new Rect(100, 100, 300, 300);
/** * 繪制矩形 * * @param rect 矩形對象 * @param paint 畫筆對象 */
canvas.drawRect(rect, redPaint);
/** * 矩形 (第三種方法) * * @param left float 矩形左側的X坐標 * @param top float 矩形頂部的Y坐標 * @param right float 矩形右側的X坐標 * @param bottom float 矩形底部的Y坐標 */
RectF rectF = new RectF((float) 100.3, 100, 300, 300);
/** * 繪制矩形 * * @param rectF 矩形對象 * @param paint 畫筆對象 */
canvas.drawRect(rectF, redPaint);
/** * 繪制矩形 (第三種方法) * * @param left float 矩形左側的X坐標 * @param top float 矩形頂部的Y坐標 * @param right float 矩形右側的X坐標 * @param bottom float 矩形底部的Y坐標 * @param paint 畫筆對象 */
canvas.drawRect((float) 100.3, 100, 300, 300, redPaint);
OK,看下效果:
Canvas 也給我們提供了圓角矩形的方法,我們來看下:
/** * 繪制圓角矩形 (第一種方法) * * @param rectF 矩形對象 * @param rx 圍繞角落的橢圓的X半徑 * @param ry 圍繞角落的橢圓的Y半徑 * @param paint 畫筆對象 */
canvas.drawRoundRect(rectF,20,20,redPaint);
/** * 繪制圓角矩形 (第二種方法) * * @param left float 矩形左側的X坐標 * @param top float 矩形頂部的Y坐標 * @param right float 矩形右側的X坐標 * @param bottom float 矩形底部的Y坐標 * @param rx 圍繞角落的橢圓的X半徑 * @param ry 圍繞角落的橢圓的Y半徑 * @param paint 畫筆對象 */
canvas.drawRoundRect((float) 100.3, 100, 300, 300,20,20,redPaint);
效果圖如下:
4、drawOval(繪制橢圓)
/** * 繪制橢圓 (第一種方法) * * @param rectF 矩形對象要繪制橢圓的矩形邊界 * @param paint 畫筆對象 */
canvas.drawOval(rectF, redPaint);
/** * 繪制橢圓 (第二種方法) * * @param left float 矩形邊界左側的X坐標 * @param top float 矩形邊界頂部的Y坐標 * @param right float 矩形邊界右側的X坐標 * @param bottom float 矩形邊界底部的Y坐標 * @param paint 畫筆對象 */
canvas.drawOval((float) 100.3, 100, 300, 200, redPaint);
5、drawCircle(繪制圓)
/** * 繪制圓 * * @param cx 要繪制的圓的中心的X坐標 * @param cy 要繪制的圓的中心的Y坐標 * @param radius 要繪制的圓的半徑 * @param paint 畫筆對象 */
canvas.drawCircle(200,200,100,redPaint);
繼續上效果圖:
6、drawArc(繪制圓弧)
/** * 繪制圓弧 * * @param oval RectF對象,橢圓的邊界用於定義弧的形狀和大小 * @param startAngle 開始的角度。(水平向右為0度順時針反向為正方向) * @param sweepAngle 掃過的角度 * @param useCenter 是否和中心連線 * @param paint 畫筆對象 */
canvas.drawArc(rectF, 0, 90, true, redPaint);
效果圖如下:
7、drawColor、drawRGB、drawARGB(繪制顏色)
/** * 繪制顏色 (第一種方法) * * @param color 顏色值 */
canvas.drawColor(Color.RED);
/** * 繪制顏色 (第二種方法) * * @param r 要繪制到畫布上的顏色的紅色成分,范圍在0-255之間 * @param g 要繪制到畫布上的顏色的綠色成分,范圍在0-255之間 * @param b 要繪制到畫布上的顏色的藍色成分,范圍在0-255之間 */
canvas.drawRGB(255, 0, 0);
/** * 繪制顏色 (第三種方法) * @param a 要繪制到畫布上的顏色的alpha分量(0-255) * @param r 要繪制到畫布上的顏色的紅色成分,范圍在0-255之間 * @param g 要繪制到畫布上的顏色的綠色成分,范圍在0-255之間 * @param b 要繪制到畫布上的顏色的藍色成分,范圍在0-255之間 */
canvas.drawARGB(255,255,0,0);
8、drawText, drawTextOnPath(繪制文本)
/** * 繪制文本 (第一種方法) * * @param text String,繪制的文本內容 * @param x float 正在繪制的文本原點的X坐標 * @param y float 正在繪制的文本原點的Y坐標 * @param paint 畫筆對象 */
canvas.drawText("繪制文本內容", 100, 100, redPaint);
/** * 繪制文本 (第二種方法) * * @param text String,繪制的文本內容 * @param start int 要從第幾個字開始繪制 * @param end int 要繪制到第幾個文字 * @param x float 正在繪制的文本原點的X坐標 * @param y float 正在繪制的文本原點的Y坐標 * @param paint 畫筆對象 */
canvas.drawText("繪制文本內容", 2,4,100, 100, redPaint);
/** * 繪制文本 (第三種方法) * * @param chars String,繪制的文本內容 * @param index int 要從第幾個字開始繪制 * @param end int 要繪制文字的數量 * @param x int 正在繪制的文本原點的X坐標 * @param y int 正在繪制的文本原點的Y坐標 * @param paint 畫筆對象 */
canvas.drawText(new char[]{'繪', '制', '文', '本', '內', '容'}, 2, 4, 100, 100, redPaint);
/** * 繪制文本 (第四種方法) * * @param text CharSequence 繪制的文本內容 * @param start int 要從第幾個字開始繪制 * @param end int 要繪制到第幾個文字 * @param x float 正在繪制的文本原點的X坐標 * @param y float 正在繪制的文本原點的Y坐標 * @param paint 畫筆對象 */
canvas.drawText("繪制文本內容", 2,4,100, 100, redPaint);
/** * 根據路徑去繪制文本 * * @param text String 繪制的文本內容 * @param path 路徑 * @param hOffset float 距離路徑開始位置的偏移量 * @param vOffset float 距離路徑上下的偏移量(可以為負數) * @param paint 畫筆對象 */
canvas.drawTextOnPath("繪制文本內容", path, 10, -50, redPaint);
/** * 根據路徑去繪制文本 * * @param text char[] 繪制的文本內容 * @param index int 要從第幾個字開始繪制 * @param end int 要繪制文字的數量 * @param path 路徑 * @param hOffset float 距離路徑開始位置的偏移量 * @param vOffset float 距離路徑上下的偏移量(可以為負數) * @param paint 畫筆對象 */
canvas.drawTextOnPath(new char[]{'繪', '制', '文', '本', '內', '容'}, 2, 4, path,10, -50, redPaint);
下面看下根據路徑繪制文本的效果:
8、drawBitmap(繪制位圖)
/** * 繪制位圖 (第一種方法) * * @param bitmap 要繪制的位圖 * @param left 繪制位圖左側的位置 * @param top 繪制位圖頂部的位置 * @param paint 畫筆對象 可以為null */
canvas.drawBitmap(bitmap, 100, 100, null);
/** * 繪制位圖 (第二種方法) * * @param bitmap 要繪制的位圖 * @param src Rect 對當前圖片進行裁剪,根據圖片繪制的區域 * @param dst Rect 對圖片的大小進行縮放和移動,根據畫布繪制顯示的區域 * @param paint 畫筆對象 可以為null */
canvas.drawBitmap(bitmap, new Rect(100, 100, bitmap.getWidth(), bitmap.getHeight()),new Rect(100, 100, bitmap.getWidth(), bitmap.getHeight()), null);
/** * 繪制位圖 (第三種方法) * * @param bitmap 要繪制的位圖 * @param src Rect 對當前圖片進行裁剪,根據圖片繪制的區域 * @param dst RectF 對圖片的大小進行縮放和移動,根據畫布繪制顯示的區域 * @param paint 畫筆對象 可以為null */
canvas.drawBitmap(bitmap, new Rect(100, 100, bitmap.getWidth(), bitmap.getHeight()),new RectF(100, 100, bitmap.getWidth(), bitmap.getHeight()), null);
/** * 繪制位圖 (第四種方法) * * @param bitmap 要繪制的位圖 * @param matrix Rect 用於在繪制位圖時變換位圖的矩陣 * @param paint 畫筆對象 可以為null */
canvas.drawBitmap(bitmap, new Matrix(), null);
下面我們看下第二種方法實現的效果:
9、drawPath(繪制路徑)
/** * 繪制路徑 * * @param path 要繪制的路徑 * @param paint 畫筆對象 */
canvas.drawPath(path, redPaint);
使用Path不僅能夠繪制簡單圖形,也可以繪制這些比較復雜的圖形。另外,根據路徑繪制文本和剪裁畫布都會用到Path。
三、畫布快照和回滾
1、SaveFlags
數據類型 | 名稱 | 簡介 |
---|---|---|
int | ALL_SAVE_FLAG | 默認,保存全部狀態 |
int | CLIP_SAVE_FLAG | 保存剪輯區 |
int | CLIP_TO_LAYER_SAVE_FLAG | 剪裁區作為圖層保存 |
int | FULL_COLOR_LAYER_SAVE_FLAG | 保存圖層的全部色彩通道 |
int | HAS_ALPHA_LAYER_SAVE_FLAG | 保存圖層的alpha(不透明度)通道 |
int | MATRIX_SAVE_FLAG | 保存Matrix信息(translate, rotate, scale, skew) |
2、Save
把當前的畫布的狀態進行保存,然后放入特定的棧中。
// 保存全部狀態
public int save ()
// 根據saveFlags參數保存一部分狀態
public int save (int saveFlags)
3.saveLayerXxx
save()方法保存的是整個Canvas,而saveLayer()則可以選擇性的保存某個區域的狀態。
// 無圖層alpha(不透明度)通道
public int saveLayer (RectF bounds, Paint paint)
public int saveLayer (RectF bounds, Paint paint, int saveFlags)
public int saveLayer (float left, float top, float right, float bottom, Paint paint)
public int saveLayer (float left, float top, float right, float bottom, Paint paint, int saveFlags)
// 有圖層alpha(不透明度)通道
public int saveLayerAlpha (RectF bounds, int alpha)
public int saveLayerAlpha (RectF bounds, int alpha, int saveFlags)
public int saveLayerAlpha (float left, float top, float right, float bottom, int alpha)
public int saveLayerAlpha (float left, float top, float right, float bottom, int alpha, int saveFlags)
4、restore
狀態回滾:用save 方法保存后,從棧頂取出一個狀態然后根據內容進行恢復,也就是說將Canvas還原成最近的一個save() 的狀態。
5、restoreToCount
save()方法還會有一個返回值,我們也可以調用restoreToCount(int saveCount)方法,將這個返回值作為參數傳遞進去,就可以將Canvas還原成某一個特定的save()狀態。
canvas.translate(100,100); // 平移(100,100)
int save1 = canvas.save(); // 保存Canvas狀態(狀態1)
canvas.scale(2, 2); // 放大2倍
int save2 = canvas.save(); // 保存Canvas狀態(狀態2)
canvas.restore(); // 返回最新的save狀態,即狀態2
canvas.restoreToCount(save1);// 手動指定的返回到 狀態1
6、getSaveCount
獲取保存的次數,即狀態棧中保存狀態的數量。
四、畫布裁剪
clipPath, clipRect
/** * 繪制路徑 * * @param path 要繪制的路徑 * @param paint 畫筆對象 */
canvas.drawPath(path, redPaint);//畫出貝塞爾曲線
/** * 畫布裁剪 * * @param RectF 與當前裁剪相交的矩形 */
canvas.clipRect(rectF);
/** * 畫布裁剪 * * @param Rect 與當前裁剪相交的矩形 */
canvas.clipRect(rect);
/** * 畫布裁剪 * * @param left float 裁剪矩形左邊的X坐標 * @param top float 裁剪矩形頂部的Y坐標 * @param right float 裁剪矩形右側的X坐標 * @param bottom float 裁剪矩形底部的Y坐標 */
canvas.clipRect(0,0,300,300);
/** * 畫布裁剪 * * @param left int 裁剪矩形左邊的X坐標 * @param top int 裁剪矩形頂部的Y坐標 * @param right int 裁剪矩形右側的X坐標 * @param bottom int 裁剪矩形底部的Y坐標 */
canvas.clipRect(0,0,300,300);
/** * 畫布裁剪 * * @param path 裁剪Path包括的范圍,Path所包括的范圍不是空的才有效 */
canvas.clipPath(path);