Android  draw canvas save restore saveLayer 學習

畫圖需要四大基本要素:
1、一個用來保存像素的Bitmap;
2、一個Canvas畫布,繪製Bitmap操作;
3、繪製的東西
4、繪製的畫筆Paint(顏色和樣式)

如何獲得一個Canvas對象?
  1. 通過重寫View.onDraw方法
  2. 自己創建一個Canvas對象
  3. 調用SurfaceHolder.lockCanvas(),返回一個Canvas 在 surfaceView 或 TextureView中使用

自己創建一個Canvas 方法如下:

//得到一個Bitmap對象,當然也可以使用別的方式得到。
//但是要注意,該bitmap一定要是mutable(異變的)
Bitmap b = Bitmap.createBitmap(100,100, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
/*先new一個Canvas對象,在調用setBitmap方法,一樣的效果
 * Canvas c = new Canvas();
 * c.setBitmap(b);
 */

canvas的常用繪製方法:
1. 填充
      drawARGB(int a, int r, int g, int b)
      drawColor(int color)
      drawRGB(int r, int g, int b)
      drawColor(int color, PorterDuff.Mode mode)
2. 幾何圖形
     canvas.drawArc (扇形)
     canvas.drawCircle(圓)
     canvas.drawOval(橢圓)
     canvas.drawLine(線)
     canvas.drawPoint(點)
     canvas.drawRect(矩形)
     canvas.drawRoundRect(圓角矩形)
     canvas.drawVertices(頂點)
     cnavas.drawPath(路徑)
3.圖片
       canvas.drawBitmap (位圖)
       canvas.drawPicture (圖片)
4.文本
       canvas.drawText

Canvas的保存和回滾
Canvas還提供了保存和回滾屬性的方法(save和restore),
比如你可以先保存目前畫紙的位置(save),然後旋轉90度,向下移動100像素後畫一些圖形,畫完後調用restore方法返回到剛纔保存的位置。

相關方法的介紹如下: 
/**
 * 保存當前的matrix和clip到私有的棧中(Skia內部實現)。任何matrix變換和clip操作都會在調用restore的時候還原。
 *
 * @return 返回值可以傳入到restoreToCount()方法,以返回到某個save狀態之前。
 */
public native int save();


/**
 * 傳入一個標誌,來表示當restore 的時候,哪些參數需要還原。該參數定義在Canvas中,參照下面。
 * save()方法默認的是還原matrix和clip,但是可以使用這個方法指定哪些需要還原。並且只有指定matrix和clip纔有效,其餘的幾個參數是
 * 用於saveLayer()和saveLayerAlpha()方法 的。
 */
public native int save(int saveFlags);


/**
 * 回到上一個save調用之前的狀態,如果restore調用的次數大於save方法,會出錯。
 */
public native void restore();

/**
 * 返回棧中保存的狀態,值等譯 save()調用次數-restore()調用次數
 */
public native int getSaveCount();


/**
 * 回到任何一個save()方法調用之前的狀態
 */
public native void restoreToCount(int saveCount);


/**
 * saveFlags的參數
 */
public static final int MATRIX_SAVE_FLAG = 0x01;//需要還原Matrix
public static final int CLIP_SAVE_FLAG = 0x02;//需要還原Clip

public static final int HAS_ALPHA_LAYER_SAVE_FLAG = 0x04;// 圖層的 clip 標記,
public static final int FULL_COLOR_LAYER_SAVE_FLAG = 0x08;// 圖層的 color 標記,
public static final int CLIP_TO_LAYER_SAVE_FLAG = 0x10;// 圖層的 clip 標記,在saveLayer 和 saveLayerAlpha Android強烈建議必須加上他
public static final int ALL_SAVE_FLAG = 0x1F; //還原所有 一般情況都是使用這個

/*關於saveLayer的具體flags還不大明白它的含義,具體怎麼使用在下面例子中*/
public int saveLayer(RectF bounds, Paint paint, int saveFlags)

public int saveLayer(float left, float top, float right, float bottom,Paint paint, int saveFlags)

public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)

public int saveLayerAlpha(float left, float top, float right, float bottom,int alpha, int saveFlags)

上圖是從網上扣下來的,  我一開始看的還是有點迷糊混亂的,下面做下解釋:
上圖 最初始 的情況 是棧裏 只有 0 1 2 3 4 ,  
然後 執行  save 方法 兩次,  則 5 和6 出現在棧中

注意,
第一次調用 save 方法的時候 返回的是 4, 這時  5 壓入棧
第二次調用 save 方法的時候 返回的是 6, 這時  6 壓入棧

save返回的 之前的棧的 數目

如果 調用  restoreToCount(5) 那麼就返回到5 的狀態了, 即  即調用 第二次 save之前的狀態

如果調用 兩次 restore 方法, 那麼會一次彈出 6 和 5  最後回到 4 的狀態

saveLayer
Canvas 在一般的情況下可以看作是一張畫布,所有的繪圖操作如drawBitmap, drawCircle都發生在這張畫布上,這張畫板還定義了一些屬性比如Matrix,顏色等等

但是如果需要實現一些相對複雜的繪圖操作,比如多層動畫,地圖(地圖可以有多個地圖層疊加而成,比如:政區層,道路層,興趣點層)。

Canvas提供了圖層(Layer)支持,缺省情況可以看作是隻有一個圖層Layer。
如果需要按層次來繪圖,Android的Canvas可以使用SaveLayerXXX, Restore 來創建一些中間層,
對於這些Layer是按照“棧結構“來管理的:   

創建一個新的Layer到“棧”中,可以使用saveLayer, savaLayerAlpha,
從“棧”中推出一個Layer,可以使用restore,restoreToCount

但Layer入棧時,後續的DrawXXX操作都發生在這個Layer上,
而Layer退棧時,就會把本層繪製的圖像“繪製”到上層或是Canvas(即最底層)上,

在繪製到上層或是Canvas(即最底層)上時,可以指定Layer的透明度(Layer)
這是在創建Layer時指定的:public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)

本例Layers 介紹了圖層的基本用法:
Canvas可以看做是由兩個圖層(Layer)構成的,爲了更好的說明問題,
我們將代碼稍微修改一下,缺省圖層繪製一個紅色的圓,
在新的圖層畫一個藍色的圓,新圖層的透明度爲0×88。
代碼如下:

public class LayersTestView extends View {
    public static final String TAG = "LayersTestView";

    private Paint mPaint;

    public LayersTestView(Context context) {
        super(context);
        init();
    }

    public LayersTestView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public LayersTestView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.WHITE);
        mPaint.setColor(Color.RED);
        canvas.drawCircle(75, 75, 75, mPaint);

        canvas.translate(25, 25);
        LogUtil.d(TAG, "getCounet2 = " + canvas.getSaveCount());
        int count = canvas.saveLayerAlpha(0, 0, 200, 200, 0x88, Canvas.ALL_SAVE_FLAG);
        LogUtil.d(TAG, "count = " + count + " , getCounet2 = " + canvas.getSaveCount());

        mPaint.setColor(Color.BLUE);
        canvas.drawCircle(125, 125, 75, mPaint);
        canvas.restore();
        canvas.drawCircle(30, 30, 30, mPaint);
        LogUtil.d(TAG, "getCounet3 = " + canvas.getSaveCount());
    }
}

一開始 在 最底層的默認  圖層上畫一個 紅色圓.
然後再 canvas.saveLayerAlpha 新建一個 不透明度爲 0x88的圖層, 在上面劃了了一個 藍色的圓 這個圖層是有透明度, 我們在手機上看到的是一個帶有透明度的 圓
然後在 canvas.restore 後,再花了一個 圓, 這個圓 是在原來的圖層上劃的, 沒有透明度 如圖: 

上面需要注意的是
  1. 我調用的canvas.translate(10, 10); 即把 畫布平移 對 新建的那個圖層是依然生效的.
  2. 我們調用 saveLayerAlpha方法, 也會和 save 方法一樣, 不僅在 layer 的那個棧中壓入一個圖層, 同時在還會在 上面 說 save 和 restore 那裏壓入一個棧.

參考鏈接:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章