Android 2D Graphics學習(二)、Canvas篇1、Canvas基本使用
請先閱讀:
Android 2D Graphics學習(一)、android.graphics介紹
Canvas的意思是畫布,表現在屏幕上就是一塊區域,我們可以再上面使用各種API繪制我們想要的東西。可以說,Canvas貫穿整個2D Graphics,android.graphics中的所有類,幾乎都于Canvas有直接或間接的聯系。所以了解Canvas是學習2D Graphics的基礎。
Android官方文檔對Canvas的簡介很好的介紹了Canvas的使用:
The Canvas class holds the "draw"calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles forthe drawing).
一個Canvas對象有四大基本要素:
1、一個用來保存像素的Bitmap
2、一個Canvas在Bitmap上進行繪制操作
3、繪制的東西
4、繪制的畫筆Paint
1、如何獲得一個Canvas對象。
Canvas對象的獲取方式有三種:
第一種我們通過重寫View.onDraw方法,View中的Canvas對象會被當做參數傳遞過來,我們操作這個Canvas,效果會直接反應在View中。
第二種就是當你想自己創建一個Canvas對象。從上面的基本要素可以明白,一個Canvas對象一定是結合了一個Bitmap對象的。所以一定要為一個Canvas對象設置一個Bitmap對象。
//得到一個Bitmap對象,當然也可以使用別的方式得到。但是要注意,改bitmap一定要是mutable(異變的) Bitmap b = Bitmap.createBitmap(100,100, Bitmap.Config.ARGB_8888); Canvas c = newCanvas(b); /*先new一個Canvas對象,在調用setBitmap方法,一樣的效果 * Canvas c = new Canvas(); * c.setBitmap(b); */
第三種方式,是調用SurfaceHolder.lockCanvas(),返回一個Canvas對象。
2、Canvas能繪制什么
Canvas類提供了一系列的draw...方法,從這些方法的名字就可以知道Canvas可以繪制的對象。
1、填充
public void drawARGB(int a, int r, int g, int b) public void drawColor(int color) public void drawRGB(int r, int g, int b) public void drawColor(int color, PorterDuff.Mode mode)
因為Canvas內部維持了一個mutable Bitmap,所以,它可以使用這些顏色去填充整個Bitmap。并且在API中提到(restricted to the current clip)受限制于clip的范圍
public void drawPaint(Paint paint)
同理,Canvas也可以使用畫筆去填充整個Bitmap,同樣和填充顏色一樣受限制于clip的范圍,API中明確指出(This is equivalent (but faster) to drawing an
infinitely large rectangle with the specified paint)這相當于用指定的畫筆畫一個更大范圍的矩形,但是速度更快。
其實在Skia內部,填充調用的都是drawPaint方法的。
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所能繪制的基本內容,在實際使用中,可以使用各種過濾或者過度模式,或者其他手段,來達到繪制各種效果。
3、Canvas的變換
如果只是那些簡單的draw...方法,那么canvas的功能就太單調了。Canvas還提供了一系列位置轉換的方法:rorate、scale、translate、skew(扭曲)等。
@Override protected void onDraw(Canvas canvas) { canvas.translate(100, 100); canvas.drawColor(Color.RED);//可以看到,整個屏幕依然填充為紅色 canvas.drawRect(new Rect(-100, -100, 0, 0), new Paint());//縮放了 canvas.scale(0.5f, 0.5f); canvas.drawRect(new Rect(0, 0, 100, 100), new Paint()); canvas.translate(200, 0); canvas.rotate(30); canvas.drawRect(new Rect(0, 0, 100, 100), new Paint());//旋轉了 canvas.translate(200, 0); canvas.skew(.5f, .5f);//扭曲了 canvas.drawRect(new Rect(0, 0, 100, 100), new Paint()); // canvas.setMatrix(matrix);//Matrix的使用在后面在是。 }
Canvas雖然內部保持了一個Bitmap,但是它本身并不代表那個Bitmap,而更像是一個圖層。我們對這個圖層的平移旋轉和縮放等等操作,并不影響內部的Bitmap,僅僅是改變了該圖層相對于內部Bitmap 的坐標位置、比例和方向而已。
4、Canvas的保存和回滾
為了方便一些轉換操作,Canvas還提供了保存和回滾屬性的方法(save和restore),比如你可以先保存目前畫紙的位置(save),然后旋轉90度,向下移動100像素后畫一些圖形,畫完后調用restore方法返回到剛才保存的位置。
Canvas提供的該功能的API如下:
/** * 保存當前的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 /**下面三個參數在saveLayer的時候使用,具體作用,沒有搞明白*/ public static final int HAS_ALPHA_LAYER_SAVE_FLAG = 0x04; public static final int FULL_COLOR_LAYER_SAVE_FLAG = 0x08; public static final int CLIP_TO_LAYER_SAVE_FLAG = 0x10; 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)
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。
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_FLAG | 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(); } } }