Android Canvas的save(),saveLayer()和restore()

1.在自定義控件當(dāng)中你onMeasure和onLayout的工作做完成以后就該繪制該控件了,有時候需要自己在控件上添加一些修飾來滿足需求

復(fù)寫onDraw(Canvas canvas),其中Canvas就像是一塊畫布,你自定義控件的樣式就是在它上面完成的。
Canvas ,Paint等基本概念就不贅述了。

2.下面就直接用demo來解釋標題列出的方法先介紹save()和saveLayer()

save(): 用來保存Canvas的狀態(tài),save()方法之后的代碼,可以調(diào)用Canvas的平移、放縮、旋轉(zhuǎn)、裁剪等操作!
restore():用來恢復(fù)Canvas之前保存的狀態(tài)(可以想成是保存坐標軸的狀態(tài)),防止save()方法代碼之后對Canvas執(zhí)行的操作,繼續(xù)對后續(xù)的繪制會產(chǎn)生影響,通過該方法可以避免連帶的影響

通過一個例子說明一下:
例如:我們想在畫布上繪制一個向右的三角箭頭,當(dāng)然,我們可以直接繪制,另外,我們也可以先把畫布旋轉(zhuǎn)90°,畫一個向上的箭頭,然后再旋轉(zhuǎn)回來(這種旋轉(zhuǎn)操作對于畫圓周上的標記非常有用),最后,我們在右下角繪一個20像素的圓!
網(wǎng)上對這個問題的解決說是旋轉(zhuǎn)回來,我的感覺其實save()保存的就是Canvas中坐標軸的狀態(tài)。
MyView:

public class MyView extends View { 
       public final static String TAG = "Example"; 
       private Paint mPaint = null; 
       public MyView(Context context) {
               super(context);
               mPaint = new Paint();
     }
      public MyView(Context context, AttributeSet attrs) {
           super(context, attrs);
    }
      public MyView(Context context, AttributeSet attrs, int defStyle) {      
                 super(context, attrs, defStyle);
      }
      @Override
      protected void onDraw(Canvas canvas) {
          super.onDraw(canvas);
          Paint background = new Paint();
          Paint line = new Paint();
          line.setStrokeWidth(4);
          background.setColor(Color.GRAY);
          line.setColor(Color.RED);
          int px = 500;
          int py = 500;
          canvas.drawRect(0, 0, px, py, background);
          canvas.save();
          canvas.rotate(90, px / 2, py / 2);
          // 畫一個向上的箭頭
          canvas.drawLine(px / 2, 0, 0, py / 2, line); 
        // 左邊的斜杠
          canvas.drawLine(px / 2, 0, px, py / 2, line);
        // 右邊的斜杠
        canvas.drawLine(px / 2, 0, px / 2, py, line);
        // 垂直的豎杠
         canvas.restore();
        canvas.drawCircle(px - 100, py - 100, 50, line);
  }
}

運行后的效果是:

Paste_Image.png

將canvas.save()和canvas.restore()這兩行代碼注釋掉以后運行的效果是:

Paste_Image.png

為什么有這種差異出現(xiàn)呢?
在 canvas.save()之前,Canvas的坐標軸是:

Paste_Image.png

save()之后就是把這種狀態(tài)的坐標軸狀態(tài)保存了下來,
canvas.rotate(90, px / 2, py / 2)圍著圓心旋轉(zhuǎn)之后,坐標軸變成:

Paste_Image.png
canvas.drawLine(px / 2, 0, 0, py / 2, line); // 左邊的斜杠
canvas.drawLine(px / 2, 0, px, py / 2, line);// 右邊的斜杠 canvas.drawLine(px / 2, 0, px / 2, py, line);// 垂直的豎杠

這一些列畫圖操作時在變換后的坐標軸上畫出來的,所以是一個往右方向的箭頭。
當(dāng)調(diào)用canvas.restore()后坐標軸恢復(fù)到canvas.save()之前的狀態(tài)。所以canvas.drawCircle(px - 100, py - 100, 50, line)參考的坐標軸是cnavas.save()之前的坐標軸。
這樣也就能說通為什么不用Canvas.save()和用Canvas.save()圓圈位置為什么不相同的原因了。

Paste_Image.png

saveLayer
Canvas 在一般的情況下可以看作是一張畫布,所有的繪圖操作如drawBitmap, drawCircle都發(fā)生在這張畫布上,這張畫板還定義了一些屬性比如Matrix,顏色等等。但是如果需要實現(xiàn)一些相對復(fù)雜的繪圖操作,比如多層動 畫,地圖(地圖可以有多個地圖層疊加而成,比如:政區(qū)層,道路層,興趣點層)。Canvas提供了圖層(Layer)支持,缺省情況可以看作是只有一個圖 層Layer。如果需要按層次來繪圖,Android的Canvas可以使用SaveLayerXXX, Restore 來創(chuàng)建一些中間層,對于這些Layer是按照“棧結(jié)構(gòu)“來管理的:

Paste_Image.png

創(chuàng)建一個新的Layer到“棧”中,可以使用saveLayer, savaLayerAlpha, 從“棧”中推出一個Layer,可以使用restore,restoreToCount。但Layer入棧時,后續(xù)的DrawXXX操作都發(fā)生在這個 Layer上,而Layer退棧時,就會把本層繪制的圖像“繪制”到上層或是Canvas上,在復(fù)制Layer到Canvas上時,可以指定Layer的 透明度(Layer),這是在創(chuàng)建Layer時指定的:public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)本例Layers 介紹了圖層的基本用法:Canvas可以看做是由兩個圖層(Layer)構(gòu)成的,為了更好的說明問題,我們將代碼稍微修改一下,缺省圖層繪制一個紅色的 圓,在新的圖層畫一個藍色的圓,新圖層的透明度為0×88。

public class Layers extends Activity { 
      @Override 
      protected void onCreate(Bundle savedInstanceState) { 
            super.onCreate(savedInstanceState);
            setContentView(new SampleView(this));
      }
      private static class SampleView extends View {
            private static final int LAYER_FLAGS = Canvas.MATRIX_SAVE_FLAG | 
            Canvas.CLIP_SAVE_FLAG11 | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG 
            | Canvas.FULL_COLOR_LAYER_SAVE_FLAG
            | Canvas.CLIP_TO_LAYER_SAVE_FLAG;

            private Paint mPaint;
            public SampleView(Context context) {
                  super(context);
                  setFocusable(true);
                  mPaint = new Paint();
                  mPaint.setAntiAlias(true);
            }
            @Override
            protected void onDraw(Canvas canvas) {
                  canvas.drawColor(Color.WHITE);
                  canvas.translate(10, 10);
                  mPaint.setColor(Color.RED);
                  canvas.drawCircle(75, 75, 75, mPaint); 
                  canvas.saveLayerAlpha(0, 0, 200, 200, 0x88, LAYER_FLAGS);
                  mPaint.setColor(Color.BLUE); 
                  canvas.drawCircle(125, 125, 75, mPaint); 
                  canvas.restore(); 
            }
      }
}

分析:canvas.saveLayerAlpha(0, 0, 200, 200, 0x88, LAYER_FLAGS)將一個layer推入棧中,后續(xù)的

mPaint.setColor(Color.BLUE);
canvas.drawCircle(125, 125, 75, mPaint);

畫一個藍色圓是在這個layer中畫的,和之前畫紅色圓的不是同一個layer層。
在canvas.restore()被保存的layer就在紅色圓layer上面了。
效果圖是:

Paste_Image.png

原文地址:http://www.cnblogs.com/liangstudyhome/p/4143498.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容