圖形繪制簡介
Android中使用圖形處理引擎,2D部分是android SDK內部自己提供,3D部分是用Open GL ES 1.0。 大部分2D使用的api都在android. graphics和android.graphics.drawable包中。他們提供了圖形處理相關的Canvas、ColorFilter、Point、RetcF等類,還有一些動畫相關的AnimationDrawable、BitmapDrawable、TransitionDrawable等。
以圖形處理來說,我們最常用到的就是在一個View上畫一些圖片、形狀或者自定義的文本內容,這些都是使用Canvas來實現的。另外,我們 可以獲取View中的Canvas對象,在繪制一些內容后調用 View.invalidate 方法讓View重新刷新,然后再繪制一個新的內容,以此多次之后,就實現了2D動畫的效果。
畫圖需要四大基本要素:1、一個用來保存像素的Bitmap2、一個或多個畫筆Paint3、需要繪制的內容4、一個Canvas畫布,用來在Bitmap上使用Paint繪制內容
Canvas對象的獲取方式
Canvas對象的獲取方式有三種:1、通過重寫View.onDraw方法獲取Canvas對象。
這種方式根據環境還分為兩種:一種是普通View的Canvas,還有一種是SurfaceView的Canvas。兩種的主要是區別就是,可以在SurfaceView中定義一個專門的線程來完成畫圖工作,應用程序不需要等待View的刷圖,提高性能。前面一種適合處理量比較小,幀率比較低的動畫方面的繪圖,比如說象棋游戲之類的;而后一種主要用在游戲或高品質動畫方面的繪圖。2、直接創建一個Canvas對象:
Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);Canvas c = new Canvas(bitmap);上面代碼創建了一個尺寸是100*100的Bitmap,使用它作為Canvas操作的對象,這時候的Canvas就是使用創建的方式。
當你使用創建的Canvas在bitmap上執行繪制方法后,你還可以將繪制的結果提交給另外一個Canvas,這樣就可以達到兩個Canvas協作完成的效果,簡化邏輯。但是android SDK建議使用View.onDraw參數里提供的Canvas就好,沒必要自己創建一個新的Canvas對象。3、調用SurfaceHolder.lockCanvas()也會返回一個Canvas對象,可以在 surfaceView 或 TextureView中使用。
Canvas位置裝換、保存、恢復
Android還提供了一些對Canvas位置轉換的方法:rorate、scale、translate、skew(扭曲)等,而且它允許你通過 getMatrix 獲得它的轉換矩陣對象,並可以直接操作它。這些操作就像是雖然你的筆還是在原來的地方畫,但是畫紙(坐標原點)旋轉或者移動了,所以你畫的東西的方位就產生了變化。
為了方便 使用 這些轉換操作,Canvas 還提供了保存和回滾屬性的方法 save和restore 。 save用來保存Canvas的狀態,save之后,可以調用Canvas的平移、放縮、旋轉、錯切、裁剪等操作。 restore 用來恢復Canvas之前保存的狀態,防止save后對Canvas執行的操作對后續的繪制有影響。注意:save和restore要配對使用,如果restore調用次數比save多,會引發Error。
小細節
上圖,最初始的情況是棧里只有 0 1 2 3 4 , 然后執行 save 方法兩次,則 5 和 6 出現在棧中
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![]()
![]()
Paint畫筆工具相關設置
Paint即畫筆,在繪圖過程中起到了極其重要的作用,畫筆主要保存了顏色、 樣式等繪制信息,指定了如何繪制文本和圖形。 畫筆對象有很多設置方法, 大體上可以分為兩類:1、圖 形相關的設置方法
- setARGB(int a,int r,int g,int b); 設置繪制的顏色,a代表透明度,r,g,b代表顏色值。
- setAlpha(int a); 設置繪制圖形的透明度。
- setColor(int color); 設置繪制的顏色,使用顏色值來表示,該顏色值包括透明度和RGB顏色。
- setAntiAlias(boolean aa); 設置是否使用抗鋸齒功能,會消耗較大資源,繪制圖形速度會變慢。
- setDither(boolean dither); 設定是否使用圖像抖動處理,會使繪制出來的圖片顏色更加平滑和飽滿,圖像更加清晰
- setFilterBitmap(boolean filter); 如果該項設置為true,則圖像在動畫進行中會濾掉對Bitmap圖像的優化操作,加快顯示速度,本設置項依賴於dither和xfermode的設置
- setMaskFilter(MaskFilter maskfilter); 設置MaskFilter,可以用不同的MaskFilter實現濾鏡的效果,如濾化,立體等
- setColorFilter(ColorFilter colorfilter); 設置顏色過濾器,可以在繪制顏色時實現不用顏色的變換效果
- setPathEffect(PathEffect effect); 設置繪制路徑的效果,如點畫線等
- setShader(Shader shader); 設置圖像效果,使用Shader可以繪制出各種漸變效果
- setShadowLayer(float radius ,float dx,float dy,int color); 在圖形下面設置陰影層,產生陰影效果,radius為陰影的角度,dx和dy為陰影在x軸和y軸上的距離,color為陰影的顏色
- setStyle(Paint.Style style); 設置畫筆的樣式。FILL:實心,默認;FILL_OR_STROKE:填充並設置描邊;STROKE:描邊,空心
- setStrokeCap(Paint.Cap cap); 定義線段斷點形狀。當畫筆樣式為STROKE或FILL_OR_STROKE時,設置我們的畫筆在【離開】畫板時候留下的最后一點圖形,如圓形樣式 Cap.ROUND(有延長),或方形樣式Cap.BUTT(默認,沒有延長)與Cap.SQUARE(有延長)
- setSrokeJoin(Paint.Join join); 設置繪制時各圖形的結合方式(圖形節點的樣式),如平滑效果等
- setStrokeWidth(float width); 當畫筆樣式為STROKE或FILL_OR_STROKE時,設置筆刷的粗細
- setXfermode(Xfermode xfermode); 設置圖形重疊時的處理方式,如合並,取交集或並集
2.文本相關的設置方法
- setFakeBoldText(boolean fakeBoldText); 模擬實現粗體文字,設置在小字體上效果會比較差
- setSubpixelText(boolean subpixelText); 設置該項為true,將有助於文本在LCD屏幕上的顯示效果
- setTextAlign(Paint.Align align); 設置繪制文字的對齊方向
- setTextScaleX(float scaleX); 設置繪制文字x軸的縮放比例,可以實現文字拉伸的效果
- setTextSize(float textSize); 設置繪制文字的字號大小
- setTextSkewX(float skewX); 設置斜體文字,skewX為x軸方向的傾斜弧度
- setTypeface(Typeface typeface); 設置字體風格,包括粗體,斜體以及襯線體,非襯線體等
- setUnderlineText(boolean underlineText); 設置帶有下划線的文字效果
- setStrikeThruText(boolean strikeThruText); 設置帶有刪除線的效果
代碼-繪制各種圖形
public class TestPaintView extends View {private Context context;private Paint paint;private RectF rect;//Rect是使用int類型作為數值,RectF是使用float類型作為數值private Path path;//主要用於繪制復雜的圖形輪廓,比如折線,圓弧以及各種復雜圖案private Bitmap bitmap;private int left, top, right, bottom;
public TestPaintView(Context context) {super(context);this.context = context;paint = new Paint();rect = new RectF(0, 0, 0, 0);//左X、上Y、右X、下Y相應的距離,即左上角、右下角的坐標,系統不會檢查數值的有效性paint.setStrokeJoin(Paint.Join.ROUND);//設置繪制時圖形的結合方式(圖形節點的樣式)paint.setStrokeCap(Paint.Cap.ROUND);//設置畫筆在【離開】畫板時留下的最后一點的樣式paint.setAntiAlias(true);paint.setDither(true);paint.setStrokeWidth(dp2px(0.5f));//設置畫筆粗細,單位為像素bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon);}
@SuppressLint("DrawAllocation")@Overrideprotected void onDraw(Canvas canvas) {//背景顏色canvas.drawColor(0x33005500);//矩形initRect(10, 50, 10, 50);paint.setStyle(Paint.Style.STROKE);//描邊(空心),默認是FILL(實心、填充)canvas.drawRect(rect, paint);//繪制弧線區域(扇形或弓形)paint.setStyle(Paint.Style.FILL);paint.setColor(0xff00ff00);canvas.drawArc(rect, 0, 90, false, paint);//當為false時是一個不經過【圓心】的弓形,當為true時是一個經過圓心的扇形paint.setColor(0xffff0000);canvas.drawArc(rect, 90, 150, true, paint); //圓弧所在矩形,起始角度90,旋轉角度150,順時針為正//矩形內切圓(或橢圓)paint.setStyle(Paint.Style.STROKE);initRect(10, 30, 10, 50);canvas.drawRect(rect, paint);paint.setARGB(255, 0, 0, 255);canvas.drawOval(rect, paint);paint.setStyle(Paint.Style.FILL);canvas.drawArc(rect, 0, 90, false, paint); //橢圓的扇形paint.setARGB(128, 255, 0, 255);canvas.drawArc(rect.left, rect.top, rect.right, rect.bottom, 90f, 150f, true, paint);
//圓角矩形paint.setStyle(Paint.Style.STROKE);initRect(10, 50, 10, 50);canvas.drawRoundRect(rect, 20, 20, paint);//矩形,兩側圓角弧度的大小,一般都設為相同//畫直線canvas.drawLine(dp2px(120), dp2px(25), dp2px(120), dp2px(55), paint);// 畫一條線(首尾兩點的坐標)paint.setColor(Color.BLUE);float[] points = new float[] { dp2px(120), dp2px(15), dp2px(150), dp2px(15),//畫多條線。每條線都需要兩個坐標(每兩個值組成一個坐標)dp2px(130), dp2px(25), dp2px(150), dp2px(55), dp2px(150), dp2px(25) };canvas.drawLines(points, paint);//最后兩個值不夠組成一條線,所以被廢棄掉了。paint.setColor(Color.BLACK);canvas.drawLines(points, 2, 8, paint);//指定跳過前2個數據,取出8個數據繪制直線// 畫圓canvas.drawCircle(dp2px(190), dp2px(35), dp2px(25), paint);//圓心坐標,半徑//畫點paint.setStrokeWidth(dp2px(3));canvas.drawPoint(dp2px(190), dp2px(35), paint);//畫一個點paint.setColor(Color.RED);canvas.drawPoints(points, paint);//畫多個點,每兩個值組成一個坐標//畫圖片,就是貼圖canvas.drawBitmap(bitmap, dp2px(220), dp2px(10), paint);//坐標指的是左上角的位置//canvas.drawCircle(320 + bitmap.getWidth() / 2, 200 + bitmap.getWidth() / 2, bitmap.getWidth() / 2, paint);//*************************************************************************************![]()
//畫路徑1,不規則封閉圖形paint.setStrokeWidth(dp2px(0.5f));path = new Path();path.moveTo(dp2px(10), dp2px(70));//指定初始輪廓點,若沒指定默認從(0,0)點開始path.lineTo(dp2px(10), dp2px(100));//從當前輪廓點繪制一條線段到指定輪廓點path.lineTo(dp2px(50), dp2px(100));path.lineTo(dp2px(50), dp2px(80));path.close(); // 回到初始點形成封閉的曲線canvas.drawPath(path, paint);//畫路徑2,利用path也可以畫各種圖形,api使用上和canvas有些許區別rect = new RectF(dp2px(60), dp2px(70), dp2px(110), dp2px(120));path.addRect(rect, Direction.CW);//利用path畫矩形。Diection.CW 順時針方向,Diection.CCW 逆時針方向canvas.drawPath(path, paint);paint.setColor(Color.BLUE);//注意,同一path繪制的圖形的顏色一定是相同的Path path2 = new Path();//如果這里不重新new一個path,則前面用path繪制的圖形的顏色也都會變path2.addRoundRect(rect, new float[] { 20, 60, 20, 60, 60, 60, 20, 20 }, Direction.CW);//從左上角順時針開始,四個角的x軸y軸方向的弧度canvas.drawPath(path2, paint);//繪制文本canvas.drawLine(rect.left, rect.centerY(), rect.right, rect.centerY(), paint);paint.reset();//重置paint.setTextSize(dp2px(12));//單位是px,只在繪制文字時有效paint.setTextAlign(Align.CENTER);//繪制的文字以drawText時指定的 float x 水平居中,默認值是Align.LEFTcanvas.drawText("1efg", rect.centerX(), rect.centerY(), paint);//注意 float y 代表的是 baseline 的值,也即e和f的下邊界,而非g的下邊界paint.setUnderlineText(true);//帶下划線canvas.drawText("包青天efg", 0, "包青天efg".length(), rect.right + dp2px(30), rect.top + dp2px(12), paint);//(+textSize)可實現和矩形頂部對齊canvas.drawText(new char[] { 'J', '!', '。', '.' }, 0, 4, rect.right + dp2px(30), rect.centerY(), paint);//在指定路徑上繪制文本Path path3 = new Path();path3.moveTo(rect.right, rect.bottom);path3.lineTo(rect.right + dp2px(150), rect.centerY());canvas.drawPath(path3, paint);paint.setTextAlign(Align.LEFT);canvas.drawTextOnPath("123456789", path3, 30, -10, paint);//float hOffset,相對基准線的向右偏移值, float vOffset向下偏移值}
/*** 重新設置矩形邊界* @param leftAdd, 代表新矩形的左邊界距離上一個矩形的右邊界的距離* @param width,代表矩形的寬度* @param topAdd, 代表新矩形的上邊界的值* @param hight,代表矩形的高度*/private void initRect(int leftAdd, int width, int topValue, int hight) {left = right + leftAdd;right = left + width;top = topValue;bottom = top + hight;rect.left = dp2px(left);rect.right = dp2px(right);rect.top = dp2px(top);rect.bottom = dp2px(bottom);}private int dp2px(float dpValue) {float scale = context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}}
代碼-位置裝換、保存、恢復
public class TestCanvasView extends View {private static final int SAVE_FLAGS = //Canvas.ALL_SAVE_FLAG //= 0x1F,還原所有,restore everything when restore() is calledCanvas.MATRIX_SAVE_FLAG//= 0x01,需要還原Matrix。restore the current matrix when restore() is called| Canvas.CLIP_SAVE_FLAG //= 0x02,需要還原Clip。restore the current clip when restore() is called| Canvas.HAS_ALPHA_LAYER_SAVE_FLAG //=0x04, 圖層的 clip 標記。the layer needs to per-pixel alpha| Canvas.FULL_COLOR_LAYER_SAVE_FLAG//= 0x08,圖層的 color 標記。the layer needs to 8-bits per color component| Canvas.CLIP_TO_LAYER_SAVE_FLAG;// = 0x10,圖層的 clip 標記,在saveLayer 和 saveLayerAlpha時Android強烈建議加上他private Paint mPaint;public TestCanvasView(Context context) {super(context);mPaint = new Paint();mPaint.setAntiAlias(true);}
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawColor(Color.WHITE);//畫一個紅色的圓mPaint.setColor(Color.RED);canvas.drawCircle(100, 100, 100, mPaint);canvas.save();//保存Canvas的狀態,save之后,可以調用Canvas的平移、放縮、旋轉、錯切、裁剪等操作。save()默認保存的是matrix和clip//畫一個綠色的拉長的圓(即橢圓)canvas.scale(0.5f, 1);//x、y軸的縮放比例mPaint.setColor(Color.GREEN);canvas.drawCircle(100, 100, 100, mPaint);//畫一個黃色的矩形canvas.restore();//restore用來恢復Canvas之前保存的狀態,防止save后對Canvas執行的指定操作對后續的繪制有影響canvas.translate(200, 0);//把畫布平移。此操作對下面新建的那個圖層依然是有效的mPaint.setColor(Color.YELLOW);canvas.drawRect(0, 0, 200, 200, mPaint);canvas.save(SAVE_FLAGS);//指定哪些需要還原。只有指定matrix或clip才有效,其余幾個參數在saveLayer和saveLayerAlpha方法中才有效![]()
//畫一個藍色的扭曲的矩形canvas.skew(2f, 1);//圖形變換唯一規則:將x坐標全部變為*2,將y坐標全部變為*1。至於是否平行啦之類的都不去限制。mPaint.setColor(Color.BLUE);canvas.drawRect(0, 0, 100, 100, mPaint);//畫一個帶透明度的藍色的旋轉的小矩形canvas.restore();//取消扭曲。canvas.translate(200, 0);canvas.rotate(30, 50, 50);//沿指定點為中心順時針旋轉指定角度。默認是以左上角為中心int count = canvas.saveLayerAlpha(0, 0, 200, 200, 0x88, SAVE_FLAGS);//在指定邊界新建一個透明度為0x88的圖層canvas.drawRect(0, 0, 100, 100, mPaint);//繪制時使用的顏色仍是上次的藍色,而非save之前的顏色。這個圖層的透明度對此圖形是有影響的//畫一個綠色的小圓canvas.restore();canvas.restore();//如果restore調用的次數大於save的調用次數,會出錯。canvas.restoreToCount(count);mPaint.setColor(Color.GREEN);canvas.drawCircle(50, 50, 50, mPaint);// 這個圓是在原來的圖層上划的, 沒有透明度}}