Canvas的功能
一. 如何在一螢幕上繪圖
三個概念:
- 需要有畫布Canvas
- 需要有筆Paint
- 需要有坐标系,畫筆預設是在左上角(0,0)位置繪制的。我們可以通過移動畫布坐标原點的形式,實作在不同位置繪制。
1. 移動坐标原點
簡單繪制一個圖
我們将畫筆Canvas.translate(100,400)移動之後再進行繪制,這時畫布的原點已經變化了。
2. 視圖坐标系
理論上 Canvas 這張紙是沒有邊界的,但是我們的手機螢幕是有界的。我們可以了解為我們透過一個方形的洞(手機螢幕)看一張巨畫(Canvas)。
而這裡我們就又存在一個問題了,因為剛才的移動,我們是移動的原點,也就是說我們的畫布是靜止不動的,隻是落筆點一直在變動,這就導緻我們繪制的圖對于使用者來說是看不全的,是以我們需要進行移動 方形的洞 來檢視這幅畫。
舉個例子,我們要檢視最開始所說的畫,可以通過移動 Screen框來檢視這幅畫,而這裡又出現了一個坐标系,這一坐标系則為 視圖坐标系,通過
scrollerTo
和
scrollerBy
進行移動該Screen框,正數則往正半軸,負數則往負半軸。
3. 小結
自定義控件中存在兩個坐标系需要明确,用一句話總結如下:
- 繪圖坐标系:決定我們的繪制的坐标
- 視圖坐标系:決定我們所看到的畫布範圍
二、Canvas的剪刀手API
Canvas 中以 clip開頭 的公有方法,用于裁剪畫布的内容。裁剪之後畫布之外将無法繪制, 我們抽取比較好玩的參數類型為Path的方法來分享,其餘的都可以一一映射進來。
1、clipPath裁剪任意形狀畫布
描述: 隻留下 path内 的畫布區域,而處于path範圍之外的則不顯示。
舉個例子: 我們先準備好一個心形的路徑Path,然後調用
clipPath
從畫布中将此路徑内的區域 “裁剪” 下來,最後為了我們觀察,使用
drawColor
“染”上酒紅色。
// 第一步:建立 心形路徑 mPath
....省略,具體請移步demo(我會貼在末尾的)
// 第二步:從畫布 canvas 裁剪下心形路徑之内的區域
canvas.clipPath(mPath);
// 第三步:塗酒紅色
canvas.drawColor(mBgColor);
【這個有一個網上的】 自帶美感的貝塞爾曲線原理與實戰
畫心型路徑基本是使用貝塞爾曲線的( 路徑關系可以參考這個畫心),路徑關系如文章所示本章僅講解和繪制相關的知識。
效果如圖所示:
代碼如下:
/**
* 參考連結:https://blog.csdn.net/BigBoySunshine/article/details/53898806
*/
public class HartView extends View {
public HartView(Context context) {
super(context);
init();
}
public HartView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public HartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
}
private Paint mPaint = new Paint();
private Path mPath = new Path();
@Override
protected void onDraw(Canvas canvas) {
int mWidth = getWidth();
int mHeight = getHeight();
for (int i = 0 + 180; i < 361 + 180; i++) {
double sit = i * 2 * Math.PI / 360d;
double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
if (i == 180) {
mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));scrcpy
}
mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
if (canvas != null)
canvas.drawPath(mPath, mPaint);
}
}
}
/**
* 參考連結:https://blog.csdn.net/BigBoySunshine/article/details/53898806
*/
public class OutsideHartView extends View {
public OutsideHartView(Context context) {
super(context);
init();
}
public OutsideHartView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public OutsideHartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
}
private Paint mPaint = new Paint();
private Path mPath = new Path();
@Override
protected void onDraw(Canvas canvas) {
int mWidth = getWidth();
int mHeight = getHeight();
for (int i = 0 + 180; i < 361 + 180; i++) {
double sit = i * 2 * Math.PI / 360d;
double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
if (i == 180) {
mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
canvas.clipOutPath(mPath);
}
canvas.drawColor(getContext().getResources().getColor(R.color.color_cd6155));
}
2. clipRect裁剪矩形
public boolean clipRect(float left, float top, float right, float bottom)
public boolean clipRect(int left, int top, int right, int bottom)
public boolean clipRect(@NonNull Rect rect)
public boolean clipRect(@NonNull RectF rect)
3. clipOutPath裁剪保留path之外的區域
描述: 隻留下 path外 的畫布區域,而處于path範圍之内的則不顯示。(與clipPath的作用範圍正好相反)
值得注意的是,該方法隻能在API26版本以上調用。 低版本我們使用下一小節介紹的方法
舉個例子:
我們先準備好一個心形的路徑Path,然後調用
clipOutPath
從畫布中将此路徑之外的區域 “裁剪” 下來,最後為了我們觀察,使用
drawColor
“染”上酒紅色。
// 第一步:建立 心形路徑 mPath
....省略,具體請移步github
// 第二步:從畫布 canvas 裁剪下心形路徑之外的區域
canvas.clipOutPath(mPath);
// 第三步:塗酒紅色
canvas.drawColor(mBgColor);
實作效果如下:
代碼如下:
/**
* 參考連結:https://blog.csdn.net/BigBoySunshine/article/details/53898806
*/
public class OutsideHartView extends View {
public OutsideHartView(Context context) {
super(context);
init();
}
public OutsideHartView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public OutsideHartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
}
private Paint mPaint = new Paint();
private Path mPath = new Path();
@Override
protected void onDraw(Canvas canvas) {
int mWidth = getWidth();
int mHeight = getHeight();
for (int i = 0 + 180; i < 361 + 180; i++) {
double sit = i * 2 * Math.PI / 360d;
double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
if (i == 180) {
mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
canvas.clipOutPath(mPath);
}
值得注意的是,該方法隻能在API26版本以上調用。 低版本我們使用下一小節介紹的方法
舉個例子:
我們先準備好一個心形的路徑Path,然後調用 clipOutPath 從畫布中将此路徑之外的區域 “裁剪” 下來,最後為了我們觀察,使用 drawColor “染”上酒紅色。
canvas.drawColor(getContext().getResources().getColor(R.color.color_cd6155));
}
此類型的方法還有以下這幾個,但他們的裁剪範圍均為矩形
public boolean clipOutRect(float left, float top, float right, float bottom)
public boolean clipOutRect(int left, int top, int right, int bottom)
public boolean clipOutRect(@NonNull Rect rect)
public boolean clipOutRect(@NonNull RectF rect)
4、clipPath 裁剪保留區域
clipPath
和
clipOutPath
一樣,都是對畫布的裁剪,不會保留白白像素,之是以這麼說是因為筆者在實際工作過程中遇到過,因為通過混色方案實作過View的壓感效果,但是如果這個View在可見區域外其實保留了空白像素,這種情況混色将會出現原本圓角位置出現四個陰影直角效果的情況。
描述: 在畫布上進行使用 path 路徑進行操作,至于其作用由 op 決定。
描述比較抽象,我們通過例子來體會。但在上例子前,我們需要先了解下
Region.Op
這個枚舉類型,具體内容代碼如下
public enum Op {
// A: 為我們先裁剪的路徑
// B: 為我們後裁剪的路徑
// A形狀中不同于B的部分顯示出來
DIFFERENCE(0),
// A和B交集的形狀
INTERSECT(1),
// A和B的全集
UNION(2),
// A和B的全集形狀,去除交集形狀之後的部分
XOR(3),
// B形狀中不同于A的部分顯示出來
REVERSE_DIFFERENCE(4),
// 隻顯示B的形狀
REPLACE(5);
// ...省略不相關代碼
}
通過源碼可以知道共有六種類型。值得一提的有以下兩點:
1)
clipOutPath
方法中使用的類型就是
DIFFERENCE
,換而言之,我們可以使用以下代碼代替,解決在API26 以下無法使用的問題
clipOutPath
方法的問題
2)
clipPath
方法中使用的類型就是
INTERSECT
,換而言之,我們可以使用以下代碼代替
clipPath(mPath, Region.Op.INTERSECT)
複制代碼
舉些例子:
接下來我們一個個講解這六種類型,兩次裁剪比較能展現出
Region.Op
參數的作用,是以我們接下來的例子需要使用兩個路徑:
1、心形路徑 (下列例子中的 A)
2、圓形路徑 (下列例子中的 B)
(1)DIFFERENCE
描述: A形狀中不同于B的部分顯示出來
效果圖: 紅色即為最終裁剪留下區域
代碼如下:
@Override
protected void onDraw(Canvas canvas) {
int mWidth = getWidth();
int mHeight = getHeight();
for (int i = 0 + 180; i < 361 + 180; i++) {
double sit = i * 2 * Math.PI / 360d;
double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
if (i == 180) {
mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
canvas.clipPath(mPath, Region.Op.DIFFERENCE);
canvas.save();
mPaint.setColor(0xaa6200EE);
canvas.drawCircle(mWidth / 2, mHeight / 2 + 400, 400, mPaint);
}
(2)INTERSECT
描述: A和B交集的形狀
效果圖: 紅色和藍色交界的部分即為最終裁剪留下區域
代碼如下:
@Override
protected void onDraw(Canvas canvas) {
int mWidth = getWidth();
int mHeight = getHeight();
for (int i = 0 + 180; i < 361 + 180; i++) {
double sit = i * 2 * Math.PI / 360d;
double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
if (i == 180) {
mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
canvas.clipPath(mPath, Region.Op.INTERSECT);
canvas.save();
mPaint.setColor(0xaa6200EE);
canvas.drawCircle(mWidth / 2, mHeight / 2 + 400, 400, mPaint);
}
(3)UNION
描述: A和B的全集,安卓版本大于等于28不支援 ,Android.P = 28;
效果圖: 紅色即為最終裁剪留下區域
效果如下:
【TODO需要補齊效果】
代碼如下:
@Override
protected void onDraw(Canvas canvas) {
int mWidth = getWidth();
int mHeight = getHeight();
for (int i = 0 + 180; i < 361 + 180; i++) {
double sit = i * 2 * Math.PI / 360d;
double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
if (i == 180) {
mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
canvas.clipPath(mPath, Region.Op.UNION);
canvas.save();
mPaint.setColor(0xaa6200EE);
canvas.drawCircle(mWidth / 2, mHeight / 2 + 400, 400, mPaint);
}
4)XOR
描述: A和B的全集形狀,去除交集形狀之後的部分 ,安卓版本大于等于28不支援 ,Android.P = 28;
效果圖: 紅色即為最終裁剪留下區域
效果如下:
【TODO需要補齊效果】
代碼如下:
@Override
protected void onDraw(Canvas canvas) {
int mWidth = getWidth();
int mHeight = getHeight();
for (int i = 0 + 180; i < 361 + 180; i++) {
double sit = i * 2 * Math.PI / 360d;
double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
if (i == 180) {
mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
canvas.clipPath(mPath, Region.Op.XOR);
canvas.save();
mPaint.setColor(0xaa6200EE);
canvas.drawCircle(mWidth / 2, mHeight / 2 + 400, 400, mPaint);
}
(5)REVERSE_DIFFERENCE
描述: B形狀中不同于A的部分顯示出來,安卓版本大于等于28不支援 ,Android.P = 28;
效果圖: 紅色即為最終裁剪留下區域
效果如下:
【TODO需要補齊效果】
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-sGSf8Y09-1627377521198)(https://user-gold-cdn.xitu.io/2019/4/27/16a5ce60957fdfdb?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)]
代碼如下:
@Override
protected void onDraw(Canvas canvas) {
int mWidth = getWidth();
int mHeight = getHeight();
for (int i = 0 + 180; i < 361 + 180; i++) {
double sit = i * 2 * Math.PI / 360d;
double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
if (i == 180) {
mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
canvas.clipPath(mPath, Region.Op.REVERSE_DIFFERENCE);
canvas.save();
mPaint.setColor(0xaa6200EE);
canvas.drawCircle(mWidth / 2, mHeight / 2 + 400, 400, mPaint);
}
(6)REPLACE
描述: 隻顯示B的形狀,安卓版本大于等于28不支援 ,Android.P = 28;
效果圖: 紅色即為最終裁剪留下區域
效果如下:
【TODO需要補齊效果】
此類
代碼如下:
@Override
protected void onDraw(Canvas canvas) {
int mWidth = getWidth();
int mHeight = getHeight();
for (int i = 0 + 180; i < 361 + 180; i++) {
double sit = i * 2 * Math.PI / 360d;
double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
if (i == 180) {
mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
}
canvas.clipPath(mPath, Region.Op.REPLACE);
canvas.save();
mPaint.setColor(0xaa6200EE);
canvas.drawCircle(mWidth / 2, mHeight / 2 + 400, 400, mPaint);
}
4. 代碼實戰
繪制心型水波紋
- 繪制心形
- 繪制波紋
- 組合
- 動畫
效果如下:
通過畫布裁剪去掉無關部分
三、了解Canvas.save(),Canvas.restore(),Canvas.restoreToCount()函數
API | 備注 |
---|---|
**Canvas save ** | 把Canvas 的資訊儲存,壓入棧 |
**Canvas restore ** | 恢複到最近的一個儲存點。出棧。 |
**restoreToCount ** | 恢複到特定的儲存點 |
目前矩陣變換
例如:平移translate(),縮放scale(),以及旋轉rotate()等
Canvas的save()、restore()這兩個方法字面意思就是儲存、恢複,但為什麼要儲存和回複呢?不儲存會怎麼樣?
其實可以了解為Canvas.store()就是将目前的Canvas壓入棧做備份,中間可能會經過若幹的矩陣變換,這些變化可能是平移、也可能是旋轉、縮放等。在我們繪制完成之後隻需要調用Canvas.restore()就可以将畫布恢複到原來的位置。但是這個操作過程中是不會改變畫布上已經繪制的圖像内容的,也就是說Path、bitmap等是不受影響的。壓棧記錄的其實是對畫布的操作。
下面我們來做一個簡單的實驗我們就可以了解了。
-
我們建立一個畫布
繪制如下Path
效果如下:protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(0xFF000000); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(10); canvas.drawLine(0, 0, 100, 0, mPaint); canvas.drawLine(0, 100, 0, 0, mPaint); canvas.translate(100, 100); canvas.drawLine(0, 0, 100, 0, mPaint); canvas.drawLine(0, 100, 0, 0, mPaint); canvas.translate(100, 100); canvas.drawLine(0, 0, 100, 0, mPaint); canvas.drawLine(0, 100, 0, 0, mPaint); }
- 增加
代碼,然後繼續繪制發現沒有任何影響Canvas.save()
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(0xFF000000); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(10); canvas.drawLine(0, 0, 100, 0, mPaint); canvas.drawLine(0, 100, 0, 0, mPaint); canvas.translate(100, 100); canvas.drawLine(0, 0, 100, 0, mPaint); canvas.drawLine(0, 100, 0, 0, mPaint); canvas.translate(100, 100); canvas.drawLine(0, 0, 100, 0, mPaint); canvas.drawLine(0, 100, 0, 0, mPaint); canvas.save(); canvas.translate(100, 100); canvas.drawLine(0, 0, 100, 0, mPaint); canvas.drawLine(0, 100, 0, 0, mPaint); }
效果如下:
- 使用
後,然後在執行重新執行繪制Canvas.restore()
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(0xFF000000);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(10);
canvas.drawLine(0, 0, 100, 0, mPaint);
canvas.drawLine(0, 100, 0, 0, mPaint);
canvas.translate(100, 100);
canvas.drawLine(0, 0, 100, 0, mPaint);
canvas.drawLine(0, 100, 0, 0, mPaint);
canvas.translate(100, 100);
canvas.drawLine(0, 0, 100, 0, mPaint);
canvas.drawLine(0, 100, 0, 0, mPaint);
canvas.save();
canvas.translate(100, 100);
canvas.drawLine(0, 0, 100, 0, mPaint);
canvas.drawLine(0, 100, 0, 0, mPaint);
canvas.restore();
canvas.translate(100, 300);
canvas.drawLine(0, 0, 100, 0, mPaint);
}
繪制效果如下:
可以看到前面設定的
canvas.translate(100, 100)
被還原了,可以得出結論,
canvas.translate(x,y)
是可以被還原的。
- 使用
還原Canvas.restore()
和Canvas.rotate()
Canvas.scale()
@Overridehttp://shubin.noip.cn:9000/image/image_20210723_400128987.png
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(0xFF000000);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(10);
canvas.drawLine(0, 0, 100, 0, mPaint);
canvas.drawLine(0, 100, 0, 0, mPaint);
canvas.translate(100, 100);
canvas.drawLine(0, 0, 100, 0, mPaint);
canvas.drawLine(0, 100, 0, 0, mPaint);
canvas.translate(100, 100);
canvas.drawLine(0, 0, 100, 0, mPaint);
canvas.drawLine(0, 100, 0, 0, mPaint);
canvas.save();
canvas.translate(100, 100);
canvas.drawLine(0, 0, 100, 0, mPaint);
canvas.drawLine(0, 100, 0, 0, mPaint);
canvas.restore();
canvas.translate(100, 300);
canvas.drawLine(0, 0, 100, 0, mPaint);
mPaint.setColor(0xffff00ff);
canvas.save();
canvas.translate(100, 100);
canvas.rotate(90);
canvas.drawLine(0, 0, 50, 0, mPaint);
canvas.drawLine(0, 50, 0, 0, mPaint);
canvas.translate(100, 100);
canvas.scale(2, 2);
mPaint.setColor(0xff0000ff);
canvas.drawLine(0, 0, 50, 0, mPaint);
canvas.drawLine(0, 50, 0, 0, mPaint);
canvas.restore();
canvas.drawLine(0, 0, 200, 0, mPaint);
}
繪制效果如下:
通過這個示例可以看出,
Canvas.rotate()
和
Canvas.scale()
都是可以被還原的。
5.裁剪整個畫布,然後執行繪制
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(0xFF000000);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(10);
mPaint.setColor(0xffff00ff);
canvas.save();http://shubin.noip.cn:9000/image/image_20210723_400128987.png
canvas.drawRect(new Rect(0, 0, 600, 600), mPaint);
canvas.translate(100, 100);
Path path = new Path();
path.addRect(new RectF(0, 0, 200, 200), Path.Direction.CW);
canvas.clipPath(path);
mPaint.setColor(0xff00ffff);
canvas.drawRect(new Rect(0, 0, 600, 600), mPaint);
canvas.restore();
}
實作效果如下:
- 通過Canvas.restore()整個畫布隻後重新繪制矩形塊。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(0xFF000000);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(10);
mPaint.setColor(0xffff00ff);
canvas.save();
canvas.drawRect(new Rect(0, 0, 600, 600), mPaint);
canvas.translate(100, 100);
Path path = new Path();
path.addRect(new RectF(0, 0, 200, 200), Path.Direction.CW);
canvas.clipPath(path);
mPaint.setColor(0xff00ffff);
canvas.drawRect(new Rect(0, 0, 600, 600), mPaint);
canvas.restore();
mPaint.setColor(0xff00ffff);
canvas.drawRect(new Rect(0, 0, 600, 600), mPaint);
}
實作效果:
Canvas的其他功能
如下圖當使用 canvas.translate(100,100)方法後canvas的坐标中心就不是左上角,而是(100,100),如圖左邊數第一個黑色直角就是 第一次canvas.translate(50,50)後的坐标中心。我用了一個for循環對同一個canvas(注意是同一個canvas)執行了5次canvas.translate(50,50),從圖中發現canvas每次都以目前坐标中心為基礎移動(50,50),如上圖。但是如果遇到在for循環中對canvas執行translate後不想canvas改變坐标中心怎麼辦?那就在canvas translate前save,後再restore。