圖形繪製 Canvas Paint Path 詳解


圖形繪製簡介
      
      
      
      
        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動畫的效果。

畫圖須要四大基本要素:
一、一個用來保存像素的Bitmap
二、一個或多個畫筆Paint
三、須要繪製的內容
四、一個Canvas畫布,用來在Bitmap上使用Paint繪製內容

Canvas對象的獲取方式
        
        
        
        
Canvas對象的獲取方式有三種:
一、經過重寫View.onDraw方法獲取Canvas對象
這種方式根據環境還分爲兩種:一種是普通View的Canvas,還有一種是SurfaceView的Canvas。
兩種的主要是區別就是,能夠在SurfaceView中定義一個專門的線程來完成畫圖工做,應用程序不須要等待View的刷圖,提升性能。
前面一種適合處理量比較小,幀率比較低的動畫方面的繪圖,好比說象棋遊戲之類的;然後一種主要用在遊戲或高品質動畫方面的繪圖。
二、直接建立一個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對象。
三、調用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可繪製的圖形種類
        
        
        
        
一、繪製背景(填充)
      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)
二、 繪製 幾何圖形
     canvas.drawArc (扇形,弓形, 弧線區域
     canvas.drawCircle(圓)
     canvas.drawOval(圓和橢圓)
     canvas.drawLine(線)
     canvas.drawPoint(點)
     canvas.drawRect(矩形)
     canvas.drawRoundRect(圓角矩形)
     canvas.drawVertices(頂點)
     cnavas.drawPath(路徑,可用來繪製任意圖形)
三、 繪製 圖片
       canvas.drawBitmap (位圖)
       canvas.drawPicture (圖片)
四、 繪製 文本
       canvas.drawText
     

Paint畫筆工具相關設置
        
        
        
        
Paint即畫筆,在繪圖過程當中起到了極其重要的做用,畫筆主要保存了顏色、 樣式等繪製信息,指定了如何繪製文本和圖形。 畫筆對象有不少設置方法,  大致上能夠分爲兩類:
一、圖 形相關的設置方法
  • 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 lefttoprightbottom;

    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")
    @Override
    protected void onDraw(Canvas canvas) {
        //背景顏色
        canvas.drawColor(0x33005500);
        //矩形
        initRect(10, 50, 10, 50);
        paint.setStyle(Paint.Style.STROKE);//描邊(空心),默認是FILL(實心、填充)
        canvas.drawRect(rectpaint);
        //繪製弧線區域(扇形或弓形)
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(0xff00ff00);
        canvas.drawArc(rect, 0, 90, falsepaint);//當爲false時是一個不通過【圓心】的弓形,當爲true時是一個通過圓心的扇形
        paint.setColor(0xffff0000);
        canvas.drawArc(rect, 90, 150, truepaint); //圓弧所在矩形,起始角度90,旋轉角度150,順時針爲正
        //矩形內切圓(或橢圓)
        paint.setStyle(Paint.Style.STROKE);
        initRect(10, 30, 10, 50);
        canvas.drawRect(rectpaint);
        paint.setARGB(255, 0, 0, 255);
        canvas.drawOval(rectpaint);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawArc(rect, 0, 90, falsepaint); //橢圓的扇形
        paint.setARGB(128, 255, 0, 255);
        canvas.drawArc(rect.leftrect.toprect.rightrect.bottom, 90f, 150f, truepaint);

        //圓角矩形
        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(pathpaint);
        //畫路徑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(pathpaint);
        paint.setColor(Color.BLUE);//注意,同一path繪製的圖形的顏色必定是相同的
        Path path2 = new Path();//若是這裏不從新new一個path,則前面用path繪製的圖形的顏色也都會變
        path2.addRoundRect(rectnew float[] { 20, 60, 20, 60, 60, 60, 20, 20 }, Direction.CW);//從左上角順時針開始,四個角的x軸y軸方向的弧度
        canvas.drawPath(path2, paint);
        //繪製文本
        canvas.drawLine(rect.leftrect.centerY(), rect.rightrect.centerY(), paint);
        paint.reset();//重置
        paint.setTextSize(dp2px(12));//單位是px,只在繪製文字時有效
        paint.setTextAlign(Align.CENTER);//繪製的文字以drawText時指定的 float x 水平居中,默認值是Align.LEFT
        canvas.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.rightrect.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 called 
    Canvas.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);
    }

    @Override
    protected 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);// 這個圓是在原來的圖層上劃的, 沒有透明度
    }
}



相關文章
相關標籤/搜索