一起看畫布Android Canvas

版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載

前言

Canvas 本意是畫布的意思,然而將它理解為繪制工具一點也不為過。通過 Canvas 提供的 API,你可以在畫布上繪制出絕大部分圖形,再配合上一些操作畫布的 API,比如旋轉(zhuǎn)剪裁等變換畫布的操作,就能夠巧妙地畫出更加復雜的圖形。本文將通過結(jié)合實例帶你深入學習 Android 中的 Canvas。如有需要進一步學習畫筆 Paint ,請移步另一篇文章—— 一起看Android Paint

drawXXX系列

canvas.drawArc

  1. 方法:

     drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint)
     drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
    
  2. 畫的方向為順時針

  3. 對參數(shù)的解釋:

    • userCenter 若為true表示此弧會和 RectF 中心相連形成扇形,否則,弧的兩頭直接相連形成圖形。
    • startAngle,負數(shù)或大于360則對360模除。
    • sweepAngle,大于360,則畫出一圈。
    • 角度:以 RectF 中心為坐標中心,中心所在直線為水平線,負角度弧斜上走,正角度弧斜下走,或者說以時鐘三點鐘為0度,順時針為正,逆時針為負。
  4. 例子:

     mPaint.setAntiAlias(true);
     mPaint.setColor(Color.RED);
     RectF mRecF=new RectF(20,20,200,200);
     canvas.drawArc(mRecF,-45,135,true,mPaint);//以斜上45度為起點,順時針掃過135度
    
  • useCenter=true
    image
  • useCenter=false
    image

canvas.drawCircle

  1. 方法:

     drawCircle(float cx, float cy, float radius, Paint paint) 
    
  2. 對參數(shù)的解釋:

    • cx,cy 為所畫圓的中心坐標,radius 為圓的半徑
  3. 例子

     mPaint.setAntiAlias(true);
     mPaint.setColor(Color.RED);
     canvas.drawCircle(100,100,80,mPaint);   
    
  • 圓心為(100,100),半徑為80
    image
  1. 注意: 當畫筆設置了 StrokeWidth 時,圓的半徑=內(nèi)圓的半徑+StrokeWidth/2

canvas.drawBitmap

  1. 方法1:

     drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) 
    

對參數(shù)的解釋:
- bitmap:要畫在畫布上的位圖
- matrix:構(gòu)建的矩陣作用于將要畫出的位圖

  1. 方法2:

     drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
    

對參數(shù)的解釋:
- src:可為 null,表示畫整個位圖,否則只花出位圖的一塊矩形區(qū)域圖.subset of bitmap
- dst:定義的一個矩形范圍,位圖會平移或縮放來將自身放入矩形內(nèi)

  1. 方法3:

     drawBitmap(Bitmap bitmap, float left, float top, Paint paint)  
    
  2. 方法4:

     drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint) 
    

網(wǎng)格扭曲,水波等的繪制
知識:https://www.zybuluo.com/cxm-2016/note/506317

  1. 例子:
  • 方法1:

          mPaint.setAntiAlias(true);
          mPaint.setColor(Color.RED);
          Matrix matrix = new Matrix();
          matrix.postTranslate(100,0);//左移100
          matrix.postRotate(45);//順時針旋轉(zhuǎn)45度
          canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher),matrix,mPaint);  
    
image
  • 方法2:

         Rect src = new Rect(20, 20, 40, 40);//取bitmap上src區(qū)域的部分圖像
         Rect dst = new Rect(100, 100, 200, 200);//繪制的最終區(qū)域,一定填滿
         mPaint.setAntiAlias(true);
         mPaint.setColor(Color.RED);
         canvas.drawBitmap(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher),src,dst,mPaint);  
    
image
  • 方法3:

           canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher), 100, 100, mPaint);  
    
image

canvas.drawColor,drawRGB

  1. 方法:

     drawColor(int color, PorterDuff.Mode mode) 
    

畫整個畫布的背景,但若區(qū)域受到剪裁,則只繪制剪裁區(qū)域的背景. 關(guān)鍵類 PorterDuff.Mode

  1. 方法:drawRGB(int r, int g, int b)
    同上

canvas.drawLine(s)

  1. 方法1:

     drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
    

對參數(shù)的解釋:
- 前四個參數(shù)為直線的起點和終點的 XY 軸坐標

  1. 方法2:

     drawLines(float[] pts,Paint paint)
    
  2. 方法3:

     drawLines(float[] pts, int offset, int count, Paint paint)  
    

對參數(shù)的解釋:
- pts:待畫的坐標點數(shù)組,格式為(x1,y1,x2,y2,...),至少4個值
- offset:要跳過坐標點數(shù)組中幾個值才開始畫(必須是4的倍數(shù))
- count:至少為2,offset 之后數(shù)組的大小。

  1. 例子
  • 方法1:

          mPaint.setAntiAlias(true);
          mPaint.setColor(Color.RED);
          mPaint.setTextSize(16);
          canvas.drawText("起點(20,100)", 22, 100, mPaint);
          canvas.drawText("終點(50,100)", 52, 150, mPaint);
          canvas.drawLine(20, 100, 50, 150, mPaint);  
    
image
  • 方法2:

          mPaint.setAntiAlias(true);
          mPaint.setColor(Color.RED);
          mPaint.setTextSize(16);
          canvas.drawText("A1(20,100)", 0, 90, mPaint);
          canvas.drawText("A2(100,350)", 40, 370, mPaint);
          canvas.drawText("B1(100,100)", 80, 90, mPaint);
          canvas.drawText("B2(180,350)", 150, 370, mPaint);
          float[] points=new float[]{20,100,100,350,100,350,100,100,100,100,180,350};//至少4個值,即能夠繪制一條直線
          canvas.drawLines(points,mPaint);  
    
image
  • 方法3:

          mPaint.setAntiAlias(true);
          mPaint.setColor(Color.RED);
          mPaint.setTextSize(16);
          canvas.drawText("A1(100,350)", 40, 370, mPaint);
          canvas.drawText("B2(100,100)", 80, 90, mPaint);
          canvas.drawText("B3(180,350)", 150, 370, mPaint);
          float[] points=new float[]{20,100,100,350,100,350,100,100,100,100,180,350};//至少4個點
          canvas.drawLines(points,4,8,mPaint);  
    
image

canvas.drawRect

  1. 方法1:

     void drawRect(float left, float top, float right, float bottom, Paint paint)  
    

確定矩形四個頂點的位置配上畫筆即可

  1. 方法2:

     void drawRect(Rect r, Paint paint) 
    

矩形的四個位置為整型時使用

  1. 方法3:

     void drawRect(RectF r, Paint paint)  
    

方法1的另一簡版,矩形的四個位置為浮點型時使用

  1. 例子:

     mPaint.setAntiAlias(true);
     mPaint.setColor(Color.RED);
     canvas.drawRect(new RectF(20f, 20f, 120f, 120f), mPaint);
     //canvas.drawRect(new Rect(20, 20, 120, 120), mPaint);  
    
image

canvas.drawOval

繪制橢圓
類似 drawRect

canvas.drawPaint

方法:
drawPaint(Paint paint)
自定義的 paint 畫在整個畫布上,等于用 paint 在畫布上畫一個無限大的矩形,但當前畫布受到剪裁,則染色區(qū)域僅限于剪裁部分。

canvas.drawPoint(s)

繪制點,方法基本類似drawLine(s)

canvas.drawRoundRect

  1. 方法1:

     drawRoundRect(RectF rect, float rx, float ry, Paint paint)   
    
  2. 方法2:

     drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)  
    

對參數(shù)的解釋:
- rx,ry 表示 leftleft+rxleftleft+ry 所圍區(qū)域做弧,其余三個角類似,當 rx=ry>=(right-left)/2 時表示畫一個半徑為 rx(ry) 的圓(剛好外接一個矩形)

  1. 例子
  • 方法1:

          mPaint.setAntiAlias(true);
          mPaint.setColor(Color.RED);
          RectF mRecf = new RectF(20, 100, 200, 200);
          canvas.drawRoundRect(mRecf, 30, 50, mPaint);  
    
image
  • 方法2:API level至少21,做法一樣

canvas.drawText

  1. 方法1:

     drawText(String text, float x, float y, Paint paint)  
    

x,y 位置開始畫 text
注意:其中 y 表示文字的基線(baseline )所在的坐標,說白了就是我們小學寫字用的那種帶有橫線的本子(一般都是按照一條基線來寫字是吧?),用于規(guī)范你寫的字是否成一條直線,否則很多人寫著寫著就往上飄了。而 x 坐標就是文字繪制的起始水平坐標,但是每個文字本身兩側(cè)都有一定的間隙,故實際文字的位置會比 x 的位置再偏右側(cè)一些。

  1. 圖:
    基線類似下圖深綠色的橫線

    image

  2. 方法2:

     drawText(CharSequence text, int start, int end, float x, float y, Paint paint)  
    

x,y 位置上畫出 startend(不含 end) 之間的字符 CharSequence charSequence = "charSequence";

  1. 方法3:

     drawText(char[] text, int index, int count, float x, float y, Paint paint)  
    

對參數(shù)的解釋:
- index:表示從第幾個字符開始,
- count:表示截取的數(shù)組長度
- 字符數(shù)組的定義: char[] a="abc".toCharArray()

  1. 方法4:

     drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint)
    

對參數(shù)的解釋:
- path:文本繪制的路徑(關(guān)鍵)
- hOffset:相對于路徑的水平偏移量
- vOffset:相對于路徑的垂直偏移量

  1. 方法5:

     drawTextOnPath(char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint)  
    

    方法3和4的合體

  2. 方法6:

     drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint paint)  
    

對參數(shù)的解釋:
- contextStart:可選,直接=start
- contextEnd:可選,直接=end
- x,y:文字繪制起點
- isRt1(isRightToLeft):文字是否支持rtl
- 0 <= contextStart <= start <= end <= contextEnd <= text.length

  1. 方法7:

     drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, float x, float y, boolean isRtl, Paint paint)  
    

方法3和方法6合體

```count = end - start, contextCount = contextEnd - contextStart.``` 
  1. 例子:
  • 方法1:

          mPaint.setAntiAlias(true);
          mPaint.setColor(Color.RED);
          mPaint.setTextSize(20);
          canvas.drawText("Canvas學習",50,100,mPaint);  
    
image
  • 方法2:

          mPaint.setAntiAlias(true);
          mPaint.setColor(Color.RED);
          mPaint.setTextSize(20);
          CharSequence charSequence="Canvas學習";
          canvas.drawText(charSequence,1,charSequence.length(),30,50,mPaint);  
    
image
  • 方法3:

          mPaint.setAntiAlias(true);
          mPaint.setColor(Color.RED);
          mPaint.setTextSize(20);
          char[] chars="Canvas學習".toCharArray();
          canvas.drawText(chars,1,chars.length-1,30,50,mPaint);  
    

效果圖同方法2

  • 方法4:

          mPaint.setAntiAlias(true);
          mPaint.setColor(Color.RED);
          mPaint.setTextSize(20);
          Path path=new Path();
          String text="Canvas學習";
          path.addCircle(100,100,50, Path.Direction.CCW);
          canvas.drawTextOnPath(text,path,0f,0f,mPaint);  
    
image

canvas.drawPath

方法:drawPath(Path path, Paint paint)
根據(jù)定義的路徑畫出圖
例子:

        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);
        Path path=new Path();
        path.addCircle(100,100,50, Path.Direction.CCW);
        canvas.drawPath(path,mPaint);  

效果等于畫一個圓

canvas.clipxxx系列

canvas.clipPath

  1. 方法1:

     clipPath(Path path)  
    

按所定義的路線剪裁,默認Region.Op.INTERSECT表示剪裁出相交的部分

  1. 方法2:

     clipPath(Path path, Region.Op op)  
    

解釋:用指定的路徑 path 修改當前的剪裁
對op參數(shù)的理解:以剪裁兩次的區(qū)域分別為A,B來區(qū)別
- Region.Op.DIFFERENCE:剪裁出差異的部分,類似 A-B 部分
- Region.Op.REPLACE:后剪裁B的覆蓋剪裁的A
- Region.Op.REVERSE_DEFFERENCE:剪裁出差異的部分,類似 B-A 部分
- Region.Op.INTERSECT:剪裁出相交的部分,類似 A交B 部分
- Region.Op.UNION:剪裁出AB合并的部分,類似** AUB**
- Region.Op.XOR:是** (AUB)-(A交B)** 剛好與** A交B** 相對

  1. 方法3:

     clipRect(Rect[F] rect, Region.Op op)[]表示可選  
    

解釋:用指定的矩形來修改當前的剪裁

  1. 方法4:

     clipRect(Rect rect)  
    

剪裁一個矩形區(qū)域,還有其他能構(gòu)造矩形的方法不再列出

  1. 例子(以剪裁路徑為例):
    首次剪裁

         Path path=new Path();
         path.addCircle(100,100,50, Path.Direction.CCW);
         canvas.clipPath(path);
         canvas.clipPath(path);
         canvas.drawColor(Color.RED);//紅色區(qū)域即為剪裁的區(qū)域  
    

由于 clipPath 方法剪裁模式默認為Region.Op.INTERSECT,故當前剪裁部分和整個畫布相交即為本身。

image

  1. 剪裁模式(綠色區(qū)域為最終得到的剪裁部分)

     mPaint.setAntiAlias(true);
     mPaint.setColor(Color.RED);
     canvas.drawColor(Color.BLUE);
     canvas.drawRect(new RectF(20, 20, 120, 120), mPaint);
     canvas.drawCircle(120, 70, 50, mPaint);
     canvas.clipRect(new RectF(20, 20, 120, 120));
     Path path = new Path();
     path.addCircle(120, 70, 50, Path.Direction.CCW);
     canvas.clipPath(path, Region.Op.INTERSECT);
     canvas.drawColor(Color.GREEN);  
    
  • Region.Op.INTERSECT
    image
  • Region.Op.REPLACE
    image
  • Region.Op.REVERSE_DEFFERENCE
    image
  • Region.Op.UNION
    image
  • Region.Op.XOR
    image

canvas的保存與恢復

  1. 解釋:用來保存或恢復 Canvas 的狀態(tài)
  2. 作用:save 之后可以調(diào)用 Canvas 的平移、放縮、旋轉(zhuǎn)、錯切、裁剪等對當前畫布進行操作,再進行相應的繪制,避免影響畫布上已繪制的 view,配合 canvas.restore()(將當前畫布恢復到初始狀態(tài)) 使用

canvas的變幻操作

canvas.translate

  1. 方法:

     canvas.translate(float dx, float dy)  
    

作用:移動當前畫布水平距離 dx,豎直距離 dy

  1. 例子:

     mPaint.setAntiAlias(true);
     mPaint.setColor(Color.RED);
     canvas.drawColor(Color.BLUE);
     canvas.translate(100,100);
     canvas.drawCircle(0,0,50,mPaint);  
    
image

canvas.scale

  1. 方法1:

     canvas.scale(float sx, float sy)  
    

作用:sx、syx、y 方向上縮放的倍數(shù),畫布縮放后,再畫出的圖片相應的坐標都會進行縮放

  1. 例子:

     mPaint.setAntiAlias(true);
     mPaint.setColor(Color.RED);
     canvas.drawColor(Color.BLUE);
     canvas.save();
     canvas.scale(0.5f,0.5f);//x,y均縮小一半
     canvas.drawCircle(100,100,50,mPaint);
     canvas.restore();
     mPaint.setColor(Color.WHITE);
     canvas.drawCircle(100,100,50,mPaint);  
    
image
  1. 方法2:

     canvas.scale (float sx, float sy, float px, float py)  
    

作用:縮放畫布并平移畫布到基準點 (px,py)
對參數(shù)的解釋:
- px,py 為縮放后畫布新的坐標原點(也叫縮放基準點)

  1. 例子:

     mPaint.setAntiAlias(true);
     canvas.drawColor(Color.BLUE);
     mPaint.setColor(Color.WHITE);
     canvas.drawCircle(100,100,50,mPaint);
     canvas.save();
     canvas.scale(0.5f,0.5f,100,100);
     mPaint.setColor(Color.RED);
     canvas.drawCircle(100,100,50,mPaint);
     canvas.restore();  
    
image

canvas.rotate

  1. 方法:

     canvas.rotate(float degrees)  
    

作用:順時針旋轉(zhuǎn)當前畫布一定角度,也可加入基準點坐標

  1. 例子:

     mPaint.setAntiAlias(true);
     canvas.drawColor(Color.BLUE);
     mPaint.setColor(Color.WHITE);
     canvas.drawRect(new RectF(80,80,180,180),mPaint);
     canvas.save();
     canvas.rotate(45);
     //canvas.rotate(45,200,200);
     mPaint.setColor(Color.RED);
     canvas.drawRect(new RectF(80,80,180,180),mPaint);
     canvas.restore();  
    
  • 無基準點

    image
  • 有基準點 (200,200)

    image

canvas.skew

  1. 方法:

     canvas.skew(float sx, float sy)  
    

作用:畫布的錯切

  • sx:將畫布在 x 方向上傾斜相應的角度,sx 為傾斜角度的 tan 值;
  • sy:將畫布在 y 軸方向上傾斜相應的角度,sy 為傾斜角度的 tan 值;
    比如在 X 軸方向上傾斜45度,tan45=1;
  1. 例子:

     mPaint.setAntiAlias(true);
     canvas.drawColor(Color.BLUE);
     mPaint.setColor(Color.WHITE);
     canvas.drawRect(new RectF(0,0,180,180),mPaint);
     canvas.save();
     canvas.skew(1,0);//畫布X軸傾斜45度
     mPaint.setColor(Color.RED);
     canvas.drawRect(new RectF(0,0,180,180),mPaint);
     canvas.restore();  
    
image

總結(jié)

以上大致介紹了 Canvas 類中眾多繪制方法。首先,先對方法進行解析;其次,給出相應的示例代碼并結(jié)合運行效果,旨在幫助讀者更好地理解諸如上述繪制方法的基本使用;最后,對于方法的理解如有紕漏,歡迎指正。

感謝

官方Canvas類API
Canvas之translate、scale、rotate、skew方法講解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容