Android Canvas的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上面了。

效果圖是:

demo下載

發佈了102 篇原創文章 · 獲贊 244 · 訪問量 74萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章