Android繪圖之Canvas狀態(tài)保存和恢復(fù)(7)

Android 繪圖學(xué)習(xí)

1 Canvas 狀態(tài)保存和恢復(fù)

前面講canvas概念理解時(shí)
已經(jīng)講解了save和savelayer,saveLayerAlpha函數(shù),這里進(jìn)行canvas狀態(tài)保存和恢復(fù)的詳細(xì)講解。

Canvas 調(diào)用了translate,scale,rotate,skew,concat or clipRect等變換后,后續(xù)的操作都是基于變換后的Canvas,都會(huì)受到影響,對(duì)于后續(xù)的操作很不方便。

Canvas提供了save,saveLayer,saveLayerAlpha,restore,restoreToCount來保存和恢復(fù)狀態(tài),有了這些函數(shù),我們就可以隨時(shí)恢復(fù)canvas以前的狀態(tài),不必?fù)?dān)心當(dāng)前操作對(duì)畫布造成的影響。

2 保存和恢復(fù)狀態(tài)函數(shù)

注意:
Canvas提供的save,saveLayer等保存狀態(tài)函數(shù)還提供了很多flag,類似MATRIX_SAVE_FLAG,CLIP_SAVE_FLAG, ALL_SAVE_FLAG等,默認(rèn)如果調(diào)用沒有flag的函數(shù)flag默認(rèn)為 ALL_SAVE_FLAG就是所有的狀態(tài)都保存,而且新的api把
帶有flag的函數(shù)都標(biāo)記成了deprecated,推薦使用不帶flag的函數(shù),進(jìn)行全部特性的保存。

2.1 save函數(shù)

Save函數(shù)保存當(dāng)前畫布的matrix,clip等信息到一個(gè)私有棧中,之后調(diào)用translate,scale,rotate,skew,concat or clipRect等操作,當(dāng)調(diào)用restore或者resoreToCount()函數(shù)后,save之后對(duì)canvas做的操作將被拋棄,會(huì)從canvas狀態(tài)棧中取出畫布的狀態(tài)信息進(jìn)行恢復(fù)。返回getSaveCount的值,沒有調(diào)用過一次save,getSaveCount值為1。

getSaveCount() ;
獲取棧中保存的狀態(tài)信息的個(gè)數(shù),計(jì)算方式等于save()的次數(shù)減去restore的次數(shù)。

調(diào)用save函數(shù)之后會(huì)返回一個(gè)saveId,表示應(yīng)該退棧的位置。

2.2 saveLayer

Save和saveLayer還有一些重載函數(shù),save和savelayer的帶saveFlag參數(shù)的函數(shù)都標(biāo)注deprecated,所以就不像其他文章一樣進(jìn)行講解了,用的也不多。


saveLayerAlpha(@Nullable RectF bounds, int alpha) 
saveLayerAlpha(float left, float top, float right, float bottom, int alpha)
saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint)
saveLayer(@Nullable RectF bounds, @Nullable Paint paint)

savelayer和saveLayerAlpha函數(shù)調(diào)用時(shí)會(huì)生成一個(gè)新的bitmap用于繪制,后續(xù)的操作都不會(huì)對(duì)原來的Canvas造成影響。調(diào)用restore或者resoreToCount()函數(shù)之后,新生成的bitmap最終會(huì)繪制到Canvas對(duì)應(yīng)的原始Bitmap上,也會(huì)從canvas狀態(tài)棧中獲取狀態(tài)信息,對(duì)canvas進(jìn)行恢復(fù)。返回getSaveCount的值,沒有調(diào)用過一次save,getSaveCount值為1。

saveLayerAlpha和saveLayer的區(qū)別只是saveLayerAlpha指定了新生成的bitmap的透明度,所以后面不會(huì)過多講解saveLayerAlpha

2.3 restore,restoreToCount

restore函數(shù):清除當(dāng)前畫布的matrix/clip狀態(tài)信息,然后從棧頂取出保存的狀態(tài)信息應(yīng)用到畫布,調(diào)用restore的次數(shù)不能超過save的次數(shù)。

Save(): 會(huì)把當(dāng)前的畫布的狀態(tài)進(jìn)行保存,然后放入Canvas狀態(tài)棧中;
saveLayer()或者saveLayerAlpha: 會(huì)把當(dāng)前畫布狀態(tài)保存放入棧中,會(huì)新建一個(gè)bitmap,后續(xù)的操作都會(huì)作用在這個(gè)bitmap上,同時(shí)可以指定新建bitmap的大小和透明度等。
restore()或restoreToCount: 就會(huì)把棧中最頂層的畫布狀態(tài)取出來,并按照這個(gè)狀態(tài)恢復(fù)當(dāng)前的畫布,并在這個(gè)畫布上做畫。

saveLayer函數(shù)如果rectF為null,有一段注釋:
May be null. The maximum size the offscreen render target needs to be (in local coordinates)
如果為null,生成的Bitmap的最大大小將和渲染目標(biāo)需要大小一樣,然而我去拿saveLayer后的canvas的大小是沒有變化的,哪位同仁知道這個(gè)大小是多少。

推薦使用save,因?yàn)閟ave不會(huì)新創(chuàng)建bitmap,saveLayer會(huì)創(chuàng)建新的bitmap,如果創(chuàng)建的bitmap過大會(huì)導(dǎo)致內(nèi)存泄漏,這點(diǎn)在savelayer函數(shù)上有說明,如果一定要使用saveLayer一定要給出確定的大小,防止內(nèi)存泄漏。

所有的save,saveLayer系列函數(shù)都有返回值,返回的是restoreToCount(),也就是調(diào)用了save次數(shù)。

沒有調(diào)用任何一次save時(shí)的canvas.getSaveCount()的值為1。save,saveLayer,savelayeralpha保存畫布信息共用一個(gè)棧,所以每次調(diào)用save函數(shù)getSaveCount函數(shù)都會(huì)加一,每次調(diào)用restore函數(shù)getSaveCount函數(shù)都會(huì)減一。調(diào)用restoreToCount(id)則會(huì)直接退棧到id標(biāo)識(shí)的canvas狀態(tài),此時(shí)在其頂部保存的狀態(tài)信息都已經(jīng)被彈棧了。

多次調(diào)用save函數(shù),可以多次進(jìn)行restore恢復(fù),restore之后進(jìn)行繪制,會(huì)在當(dāng)前狀態(tài)canvas畫布上進(jìn)行繪制,受當(dāng)前Canvas狀態(tài)的影響。

重要:
調(diào)用save或者saveLayer系列函數(shù)是有返回值的,這個(gè)返回值就可以作為restoreToCount的函數(shù)實(shí)參,可以返回到保存之前的畫布狀態(tài)。

例如調(diào)用save或者saveLayer后返回saveId為2,那么現(xiàn)在getSaveCount的值應(yīng)該為3,此時(shí)直接調(diào)用restoreToCount(2),就可以返回調(diào)用save或者saveLayer之前的狀態(tài),而且可以保存這個(gè)獲取到的saveId值,在特定的位置利用restoreToCount(saveId),就可以回到生成這個(gè)saveId之前的狀態(tài)。

總結(jié):
restore ,restoreToCount兩個(gè)函數(shù)都是用于恢復(fù)畫布,restore直接取保存在棧中的棧頂?shù)漠嫴紶顟B(tài)進(jìn)行恢復(fù),restoreToCount:是對(duì)restore的封裝,可以直接彈棧直到目標(biāo)位置的畫布狀態(tài),當(dāng)saveCount小于1時(shí)會(huì)報(bào)錯(cuò)。

3代碼示例

canvas.drawRect(200,200,700,700,mPaint1);
mPaint1.setColor(Color.GRAY);
Matrix matrix = new Matrix();
matrix.setTranslate(100,100);
canvas.setMatrix(matrix);
canvas.drawRect(200,200,700,700,mPaint1);
canvas.drawRect(0,0,100,100,mPaint1);

最后一句想在原點(diǎn)繪制圖形,無法回到繪制第一個(gè)矩形時(shí)的原點(diǎn)。

調(diào)用了save,restore。

canvas.drawRect(200,200,700,700,mPaint1);
canvas.save();
mPaint1.setColor(Color.GRAY);
Matrix matrix = new Matrix();
matrix.setTranslate(100,100);
canvas.setMatrix(matrix);
canvas.drawRect(200,200,700,700,mPaint1);
canvas.restore();
canvas.drawRect(0,0,100,100,mPaint1);

saveLayer(@Nullable RectF bounds, @Nullable Paint paint)
saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint)
Paint可以不傳。
生成一個(gè)(0,0,100,100)的bitmap

canvas.drawRect(200,200,700,700,mPaint1);
canvas.saveLayer(0,0,100,100,mPaint1);
mPaint1.setColor(Color.GRAY);
Matrix matrix = new Matrix();
matrix.setTranslate(100,100);
canvas.setMatrix(matrix);
canvas.drawRect(200,200,700,700,mPaint1);//繪制不上,因?yàn)椴辉谏傻腷itmap范圍
canvas.restore();
canvas.drawRect(0,0,100,100,mPaint1);

可以繪制上的版本。

canvas.drawRect(200,200,700,700,mPaint1);
canvas.saveLayer(0,0,700,700,mPaint1);
mPaint1.setColor(Color.GRAY);
Matrix matrix = new Matrix();
matrix.setTranslate(100,100);
canvas.setMatrix(matrix);
canvas.drawRect(200,200,700,700,mPaint1);//由于translate的原因,繪制不全
canvas.restore();
canvas.drawRect(0,0,100,100,mPaint1);

** getSaveCount:**

canvas.drawRect(200,200,700,700,mPaint1);
System.out.println("==========daxiao11111=========="+canvas.getSaveCount());
//canvas.saveLayer(0,0,700,700,mPaint1);
canvas.saveLayer(null,mPaint1);
System.out.println("==========daxiao222========="+canvas.getSaveCount());
mPaint1.setColor(Color.GRAY);
Matrix matrix = new Matrix();
matrix.setTranslate(100,100);
canvas.setMatrix(matrix);
canvas.drawRect(200,200,700,700,mPaint1);
canvas.restore();
System.out.println("==========daxiao333========="+canvas.getSaveCount());
canvas.drawRect(0,0,100,100,mPaint1);

canvas.saveLayer(100, 100, 200, 200, mPaint1);
canvas.drawRect(0,0,100,100,mPaint1);
canvas.restore();

result:
==========daxiao11111=========1
==========daxiao222=========2
==========daxiao333=========1

多次save,多次restore:

  canvas.save();//第一次保存
        canvas.translate(200,200);
        canvas.save();// 保存第二次
        canvas.translate(200,200);
        canvas.save();//第三次保存
        canvas.translate(200,200);
        canvas.save();//第四次保存
        canvas.translate(200,200);
        canvas.save();//第五次保存
        canvas.translate(200,200);
        canvas.save();

        mPaint1.setColor(Color.RED);
        canvas.drawRect(0,0,100,100,mPaint1);

        canvas.restore();
        mPaint1.setColor(Color.YELLOW);
        canvas.drawRect(0,0,100,100,mPaint1);

        canvas.restore();
        mPaint1.setColor(Color.GREEN);
        canvas.drawRect(0,0,100,100,mPaint1);

        canvas.restore();
        mPaint1.setColor(Color.BLUE);
        canvas.drawRect(0,0,100,100,mPaint1);

        canvas.restore();
        mPaint1.setColor(Color.GRAY);
        canvas.drawRect(0,0,100,100,mPaint1);

        canvas.restore();
        mPaint1.setColor(Color.BLACK);
        canvas.drawRect(0,0,100,100,mPaint1);

可以看到,每次restore,恢復(fù)之前的canvas畫布狀態(tài)。

restoreToCount
上面的例子,如果restore一次,如何畫藍(lán)色矩形。

  canvas.save();//第一次保存,count2
        canvas.translate(200,200);
        canvas.save();// 保存第二次,count3
        canvas.translate(200,200);
        canvas.save();//第三次保存
        canvas.translate(200,200);
        canvas.save();//第四次保存
        canvas.translate(200,200);
        canvas.save();//第五次保存
        canvas.translate(200,200);
        canvas.save();
        //直接4,就可以回到畫藍(lán)色矩形位置
        canvas.restoreToCount(4);
        mPaint1.setColor(Color.BLUE);
        canvas.drawRect(0,0,100,100,mPaint1);

總結(jié):

  • 所有的save函數(shù)(save,saveLayer,saveLayerAlpha)公用一個(gè)棧,每次save函數(shù)會(huì)把Canvas狀態(tài)信息保存在棧頂
  • 進(jìn)入onDraw沒有調(diào)用save函數(shù)前getSaveCount值為1,
  • restore 每次取棧頂Cavas狀態(tài)
  • restoreToCount(id) 直接出棧id之前的所有狀態(tài),恢復(fù)調(diào)用save或者saveLayer返回id之前的對(duì)應(yīng)棧中Canvas的狀態(tài)
  • saveLayer和saveLayerAlpha會(huì)生成一個(gè)新的Bitmap,可以指定大小和透明度,但可能引起內(nèi)存泄漏要控制好生成bitmap的大小。
  • 多次save可以多次restore,調(diào)用restore的次數(shù)不能超過save的次數(shù)

4 狀態(tài)保存saveFlag

雖然 帶有saveFlag的函數(shù)都標(biāo)記了過時(shí),但是還是可以使用的,下面簡介各個(gè)flag的作用:

ALL_SAVE_FLAG
保存全部的狀態(tài),默認(rèn)的save行為,可以用于save,saveLayer函數(shù)。

CLIP_SAVE_FLAG
保存裁剪的某個(gè)區(qū)域的狀態(tài),只保存尺寸狀態(tài),不保存matrix,所以調(diào)用restore恢復(fù)后,旋轉(zhuǎn)都操作依然影響canvas,clip操作不影響。可以用于save,saveLayer函數(shù)。

MATRIX_SAVE_FLAG
Matrix信息(translate,rotate,scale,skew)的狀態(tài)保存,不保存尺寸信息,調(diào)用restore后,clip依然影響畫布,matrix操作則不影響畫布??梢杂糜趕ave,saveLayer函數(shù)。

后面的三個(gè)flag只作用于saveLayer函數(shù)
CLIP_TO_LAYER_SAVE_FLAG
保存預(yù)先設(shè)置的范圍里的狀態(tài)

FULL_COLOR_LAYER_SAVE_FLAG
保存彩色涂層

HAS_ALPHA_LAYER_SAVE_FLAG
不透明圖層保存

android繪圖之Paint(1)
android繪圖之Canvas基礎(chǔ)(2)
Android繪圖之Path(3)
Android繪圖之drawText繪制文本相關(guān)(4)
Android繪圖之Canvas概念理解(5)
Android繪圖之Canvas變換(6)
Android繪圖之Canvas狀態(tài)保存和恢復(fù)(7)
Android繪圖之PathEffect (8)
Android繪圖之LinearGradient線性漸變(9)
Android繪圖之SweepGradient(10)
Android繪圖之RadialGradient 放射漸變(11)
Android繪制之BitmapShader(12)
Android繪圖之ComposeShader,PorterDuff.mode及Xfermode(13)
Android繪圖之drawText,getTextBounds,measureText,FontMetrics,基線(14)
Android繪圖之貝塞爾曲線簡介(15)
Android繪圖之PathMeasure(16)
Android 動(dòng)態(tài)修改漸變 GradientDrawable

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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