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

畫圖需要四大基本要素:
1、一個用來保存像素的Bitmap
2、一個或多個畫筆Paint
3、需要繪制的內容
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 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);// 這個圓是在原來的圖層上划的, 沒有透明度
    }
}






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM