save() saveLayer() restore()
1.在自定義控件當中你onMeasure和onLayout的工作做完成以后就該繪制該控件了,有時候需要自己在控件上添加一些修飾來滿足需求
復寫onDraw(Canvas canvas),其中Canvas就像是一塊畫布,你自定義控件的樣式就是在它上面完成的。
Canvas ,Paint等基本概念就不贅述了。
2.下面就直接用demo來解釋標題列出的方法先介紹save()和
必須了解的相關知識:http://www.cnblogs.com/liangstudyhome/p/4126002.html
save() : 用來保存Canvas的狀態,save()方法之后的代碼,可以調用Canvas的平移、放縮、旋轉、裁剪等操作!
restore():用來恢復Canvas之前保存的狀態(可以想成是保存坐標軸的狀態),防止save()方法代碼之后對Canvas執行的操作,繼續對后續的繪制會產生影響,通過該方法可以避免連帶的影響
通過一個例子說明一下:
例如:我們想在畫布上繪制一個向右的三角箭頭,當然,我們可以直接繪制,另外,我們也可以先把畫布旋轉90°,畫一個向上的箭頭,然后再旋轉回來(這種旋轉操作對於畫圓周上的標記非常有用),最后,我們在右下角繪一個20像素的圓!
網上對這個問題的解決說是旋轉回來,我的感覺其實save()保存的就是Canvas中坐標軸的狀態。
MyView:
1 public class MyView extends View { 2 3 public final static String TAG = "Example"; 4 5 private Paint mPaint = null; 6 7 public MyView(Context context) { 8 super(context); 9 mPaint = new Paint(); 10 } 11 12 public MyView(Context context, AttributeSet attrs) { 13 super(context, attrs); 14 } 15 16 public MyView(Context context, AttributeSet attrs, int defStyle) { 17 super(context, attrs, defStyle); 18 } 19 20 @Override 21 protected void onDraw(Canvas canvas) { 22 super.onDraw(canvas); 23 Paint background = new Paint(); 24 Paint line = new Paint(); 25 line.setStrokeWidth(4); 26 background.setColor(Color.GRAY); 27 line.setColor(Color.RED); 28 29 int px = 500; 30 int py = 500; 31 32 canvas.drawRect(0, 0, px, py, background); 33 canvas.save(); 34 canvas.rotate(90, px / 2, py / 2); 35 // 畫一個向上的箭頭 36 canvas.drawLine(px / 2, 0, 0, py / 2, line); // 左邊的斜杠 37 canvas.drawLine(px / 2, 0, px, py / 2, line);// 右邊的斜杠 38 canvas.drawLine(px / 2, 0, px / 2, py, line);// 垂直的豎杠 39 40 canvas.restore(); 41 canvas.drawCircle(px - 100, py - 100, 50, line); 42 43 } 44 45 }
運行后的效果是:
將canvas.save()和canvas.restore()這兩行代碼注釋掉以后運行的效果是:
為什么有這種差異出現呢?
在 canvas.save()之前,Canvas的坐標軸是:
save()之后就是把這種狀態的坐標軸狀態保存了下來,
canvas.rotate(90, px / 2, py / 2)圍着圓心旋轉之后,坐標軸變成:

1 canvas.drawLine(px / 2, 0, 0, py / 2, line); // 左邊的斜杠 2 canvas.drawLine(px / 2, 0, px, py / 2, line);// 右邊的斜杠 3 canvas.drawLine(px / 2, 0, px / 2, py, line);// 垂直的豎杠
這一些列畫圖操作時在變換后的坐標軸上畫出來的,所以是一個往右方向的箭頭。
當調用canvas.restore()后坐標軸恢復到canvas.save()之前的狀態。所以canvas.drawCircle(px - 100, py - 100, 50, line)參考的坐標軸是cnavas.save()之前的坐標軸。
這樣也就能說通為什么不用Canvas.save()和用Canvas.save()圓圈位置為什么不相同的原因了。
saveLayer
Canvas 在一般的情況下可以看作是一張畫布,所有的繪圖操作如drawBitmap, drawCircle都發生在這張畫布上,這張畫板還定義了一些屬性比如Matrix,顏色等等。但是如果需要實現一些相對復雜的繪圖操作,比如多層動 畫,地圖(地圖可以有多個地圖層疊加而成,比如:政區層,道路層,興趣點層)。Canvas提供了圖層(Layer)支持,缺省情況可以看作是只有一個圖 層Layer。如果需要按層次來繪圖,Android的Canvas可以使用SaveLayerXXX, Restore 來創建一些中間層,對於這些Layer是按照“棧結構“來管理的:
創建一個新的Layer到“棧”中,可以使用saveLayer, savaLayerAlpha, 從“棧”中推出一個Layer,可以使用restore,restoreToCount。但Layer入棧時,后續的DrawXXX操作都發生在這個 Layer上,而Layer退棧時,就會把本層繪制的圖像“繪制”到上層或是Canvas上,在復制Layer到Canvas上時,可以指定Layer的 透明度(Layer),這是在創建Layer時指定的:public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)本例Layers 介紹了圖層的基本用法:Canvas可以看做是由兩個圖層(Layer)構成的,為了更好的說明問題,我們將代碼稍微修改一下,缺省圖層繪制一個紅色的 圓,在新的圖層畫一個藍色的圓,新圖層的透明度為0×88。
1 public class Layers extends Activity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(new SampleView(this)); 7 } 8 9 private static class SampleView extends View { 10 private static final int LAYER_FLAGS = Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG 11 | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG 12 | Canvas.CLIP_TO_LAYER_SAVE_FLAG; 13 14 private Paint mPaint; 15 16 public SampleView(Context context) { 17 super(context); 18 setFocusable(true); 19 20 mPaint = new Paint(); 21 mPaint.setAntiAlias(true); 22 } 23 24 @Override 25 protected void onDraw(Canvas canvas) { 26 canvas.drawColor(Color.WHITE); 27 canvas.translate(10, 10); 28 mPaint.setColor(Color.RED); 29 canvas.drawCircle(75, 75, 75, mPaint); 30 canvas.saveLayerAlpha(0, 0, 200, 200, 0x88, LAYER_FLAGS); 31 mPaint.setColor(Color.BLUE); 32 canvas.drawCircle(125, 125, 75, mPaint); 33 canvas.restore(); 34 } 35 } 36 }
分析:canvas.saveLayerAlpha(0, 0, 200, 200, 0x88, LAYER_FLAGS)將一個layer推入棧中,后續的
1 mPaint.setColor(Color.BLUE); 2 canvas.drawCircle(125, 125, 75, mPaint);
畫一個藍色圓是在這個layer中畫的,和之前畫紅色圓的不是同一個layer層。
在canvas.restore()被保存的layer就在紅色圓layer上面了。
效果圖是: