自定義View-第四步:畫布操作

前言

根據(jù)Gcssloop所學(xué)習(xí)的自定義View整理與筆記。
還記得上一篇參考補(bǔ)充小節(jié)canvas的各種方法么?本章內(nèi)容就是用來講解cavas的各種方法的哦~

大大要點(diǎn)

所有的畫布操作都只影響后續(xù)的繪制,對(duì)之前已經(jīng)繪制過的內(nèi)容沒有影響
所有的畫布操作都只影響后續(xù)的繪制,對(duì)之前已經(jīng)繪制過的內(nèi)容沒有影
所有的畫布操作都只影響后續(xù)的繪制,對(duì)之前已經(jīng)繪制過的內(nèi)容沒有影響
切記切記,重要事情說三遍?。。?/p>

一、位移translate方法

首先看下面的代碼

paint.setColor(Color.BLUE);
//位移
canvas.translate(200, 300);
//在坐標(biāo)原點(diǎn)畫一個(gè)藍(lán)色的圓形
canvas.drawCircle(0,0,100,paint);
paint.setColor(Color.MAGENTA);
//位移
canvas.translate(200, 300);
//在坐標(biāo)原點(diǎn)畫一個(gè)粉紅色的圓形
canvas.drawCircle(0,0,100,paint);

效果如下:



由此,我們可以看出來:

位移是基于當(dāng)前位置移動(dòng),而不是每次基于屏幕左上角的(0,0)點(diǎn)移動(dòng),初始默認(rèn)是屏幕左上角。

可以這樣想,坐標(biāo)軸控制繪畫的位置,translate的時(shí)候,可以想象一個(gè)活動(dòng)的坐標(biāo)軸在移動(dòng)。我們拿著坐標(biāo)軸的中心點(diǎn)進(jìn)行位置移動(dòng),每一次的移動(dòng)都以上一次的坐標(biāo)軸為參考物。

二、縮放scale方法

首先,看一下縮放方法:

//1
public void scale (float sx, float sy);
//2
public final void scale(float sx, float sy, float px, float py) {
    translate(px, py);
    scale(sx, sy);
    translate(-px, -py);
}

參數(shù)含義:

  • sx: x軸的縮放比例,縮放中心默認(rèn)坐標(biāo)軸原點(diǎn),縮放前的值*sx的絕對(duì)值=縮放后的值;如果sx<0,則再根據(jù)x軸進(jìn)行翻轉(zhuǎn)
  • sy: y軸的縮放比例,縮放中心默認(rèn)坐標(biāo)軸原點(diǎn),縮放前的值*sy的絕對(duì)值=縮放后的值;如果sy<0,則根據(jù)y軸進(jìn)行翻轉(zhuǎn)
  • px,py: 設(shè)置縮放中心

首先要注意以下幾點(diǎn):

  1. cavas的操作效果是疊加的,即影響的該畫布之后的所有組件!
  2. translate(px, py)移動(dòng)的物理距離分別是px和py,經(jīng)過scale(sx, sy)縮放后再通過translate(-px, -py)位移,移動(dòng)的物理距離就是-px*sx和-py*sy。

看著覺得疑惑,不要擔(dān)心,我們下邊跟著代碼會(huì)理解的哦O(∩_∩)O~

為了更好地理解,我們可以參考下邊的demo:
先位移至一定位置,繪制藍(lán)色方框,縮放后,繪制黑色方框

//位移,繪制藍(lán)色,縮放后,繪制黑色方框
canvas.translate(200, 300);
paint.setColor(Color.BLUE);
canvas.drawRect(new RectF(0,0,30,30),paint);
paint.setColor(Color.BLACK);
canvas.scale(-0.5f,-1,100,0);
canvas.drawRect(new RectF(0,0,30,30),paint);

上邊的代碼可以用下邊的代碼代替,為了方便理解,我們每一個(gè)分解步驟,都繪制一個(gè)方框,一步步跟著代碼走。

canvas.translate(200, 300);
paint.setColor(Color.BLUE);
canvas.drawRect(new RectF(0,0,30,30),paint);
canvas.translate(100,0);
paint.setColor(Color.MAGENTA);
canvas.drawRect(new RectF(0,0,30,30),paint);
canvas.scale(0.5f,1);
paint.setColor(Color.YELLOW);
canvas.drawRect(new RectF(0,0,30,30),paint);
canvas.scale(-1,-1);
paint.setColor(Color.GRAY);
canvas.drawRect(new RectF(0,0,30,30),paint);
canvas.translate(-100,0);
paint.setColor(Color.BLACK);
canvas.drawRect(new RectF(0,0,30,30),paint);

效果圖如下

非分解

分解

可以看出,這兩種代碼的效果是一樣的,
總之,我們可以這樣理解,每一次的操作,我們都進(jìn)行了一次畫布的變動(dòng),這次的變動(dòng)對(duì)于前邊的繪制沒有任何影響,但對(duì)后邊的所有操作都有影響。

現(xiàn)在,我們?cè)谔砑右恍┬麓a和圖理解一下

canvas.translate(200, 300);
paint.setColor(Color.BLUE);
canvas.drawRect(new RectF(0,0,30,30),paint);
paint.setColor(Color.BLACK);
canvas.scale(-0.5f,-1,100,0);
canvas.drawRect(new RectF(0,0,30,30),paint);
//新代碼
paint.setColor(Color.RED);
canvas.translate(100,100);
canvas.drawRect(new RectF(0,0,30,30),paint);

效果:


三、旋轉(zhuǎn)rotate

public void rotate (float degrees);
public final void rotate(float degrees, float px, float py) {
    translate(px, py);
    rotate(degrees);
    translate(-px, -py);
}
  • 默認(rèn)旋轉(zhuǎn)中心為原點(diǎn),正值順時(shí)針
canvas.translate(300, 300);
canvas.drawCircle(0,0,200,paint);
canvas.drawCircle(0,0,180,paint);
for (int i = 0; i <=360; i=i+10) {
    canvas.drawLine(-200,0,-180,0,paint);
    canvas.rotate(10);
}

四、錯(cuò)切skew

public void skew (float sx, float sy)
  • float sx:將畫布在x方向上傾斜相應(yīng)的角度,sx傾斜角度的tan值
  • float sy:將畫布在y軸方向上傾斜相應(yīng)的角度,sy為傾斜角度的tan值.
canvas.translate(300, 300);
paint.setColor(Color.GREEN);
canvas.drawRect(new RectF(0,0,100,100),paint);
paint.setColor(Color.BLACK);
canvas.skew(0.5f,0.5f);
canvas.drawRect(new RectF(0,0,100,100),paint);
paint.setColor(Color.RED);
canvas.skew(0.5f,0.5f);
canvas.drawRect(new RectF(0,0,100,100),paint);

五、保存和恢復(fù)畫布狀態(tài)

1. 保存save和恢復(fù)restore

  • Save():每次調(diào)用Save()函數(shù),都會(huì)把當(dāng)前的畫布的狀態(tài)進(jìn)行保存,即保存當(dāng)前Canvas的狀態(tài),然后放入特定的棧中,這樣之后,我們就可以調(diào)用Canvas的平移、放縮、旋轉(zhuǎn)、錯(cuò)切、裁剪等操作啦;

  • restore():每當(dāng)調(diào)用Restore()函數(shù),就會(huì)把棧中最頂層的畫布狀態(tài)取出來,并按照這個(gè)狀態(tài)恢復(fù)當(dāng)前的畫布,在這個(gè)畫布上做畫,也可以說,是用來恢復(fù)Canvas之前保存的狀態(tài),防止save后對(duì)Canvas執(zhí)行的操作對(duì)后續(xù)的繪制有影響。

注意:save和restore要配對(duì)使用(restore可以比save少,但不能多),如果restore調(diào)用次數(shù)比save多,會(huì)引發(fā)Error。

canvas.drawColor(Color.RED);
//保存的畫布大小為全屏幕大小
canvas.save();
//要在畫圖之前對(duì)canvas進(jìn)行clip,如果畫圖之后再對(duì)canvas進(jìn)行clip不會(huì)影響到已經(jīng)畫好的圖形。一定要記住clip是針對(duì)canvas而非圖形
canvas.clipRect(new Rect(0, 0, 400, 400));
canvas.drawColor(Color.GREEN);
//保存畫布大小為Rect(0, 0, 500, 500)
canvas.save();

canvas.clipRect(new Rect(50, 50, 350, 350));
canvas.drawColor(Color.BLUE);
canvas.save();

canvas.clipRect(new Rect(100, 100, 300, 300));
canvas.drawColor(Color.YELLOW);
canvas.save();

canvas.clipRect(new Rect(150, 150, 250, 250));
canvas.drawColor(Color.MAGENTA);

效果圖:


在上述代碼下邊加上如下代碼

//將棧頂?shù)漠嫴紶顟B(tài)取出來,作為當(dāng)前畫布,并畫成黃色背景
canvas.restore();  //①
canvas.drawColor(Color.WHITE); //②

如果只添加①的話,顯示效果沒有變化的,添加①②的話,則效果如下:


補(bǔ)充:Clip系列函數(shù)如下:

boolean clipPath(Path path)
boolean clipPath(Path path, Region.Op op)
boolean clipRect(Rect rect, Region.Op op)
boolean clipRect(RectF rect, Region.Op op)
boolean clipRect(int left, int top, int right, int bottom)
boolean clipRect(float left, float top, float right, float bottom)
boolean clipRect(RectF rect)
boolean clipRect(float left, float top, float right, float bottom, Region.Op op)
boolean clipRect(Rect rect)
boolean clipRegion(Region region)
boolean clipRegion(Region region, Region.Op op)

后記

每一次的變動(dòng),可以理解為在改變畫布的坐標(biāo)軸??

參考網(wǎng)址

canvas變換與操作

感謝大家的批評(píng)指正~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容