Canvas类和Paint类

  参考 Android 8.0 源码

/framework/base/graphics/java/android/graphics/Paint.java
/framework/base/graphics/java/android/graphics/BaseCanvas.java
/framework/base/graphics/java/android/graphics/Canvas.java
public class Paint {

}
public class Canvas extends BaseCanvas {

}

  Canvas意思是画布,Canvas提供了若干种方法用于绘制各种图形图案--点、线、圆等。Paint意思是画笔,为绘图定义各种参数--颜色、线条样式、图案样式等。通常的绘图思路是先定义Paint对象,指定绘图参数,再通过Canvas对象进行图形绘制,绘图的结果因Paint的不同而不同。

  Canvas类定义绘图方法,Paint类指定绘图参数。

Paint类

  Paint类用于定义绘图时的参数,主要包含颜色、文本、图形样式、位图模式、滤镜等几个方面。通过控制这些参数,我们可以设计出简单实用的图片编辑器。

  颜色是指绘图时使用的颜色,在Android中颜色可以指定透明度,使用16进制来表示颜色时,格式通常为#AARRGGBB,其中,AA表示透明度、RR表示红色、GG表示绿色、BB表示蓝色。Color类定义了颜色信息,内置了常用颜色的int型常量,比如Color.RED是红色,Color.BLUE是蓝色。Color类的静态方法parseColor(String colorString)可以将16进制颜色转换为Color类型。需要注意的是,Android的颜色都是int类型的,Color类只负责颜色的管理而不是代表某种颜色。

//将16进制的颜色转换成Android认可的颜色值;
int color = Color.parseColor("#66FF0000");

  Paint类中与颜色有关的方法:

//设置颜色;
public void setColor(@ColorInt int color)

//设置透明度,a的范围取0~255之间的整数;
public void setAlpha(int a)

//指定透明度、红色、绿色、蓝色,来定义一种颜色;
public void setARGB(int a, int r, int g, int b) 

  绘制文本时,可以指定文本的大小、对齐方式、文本样式等属性,文本样式主要是为文本指定粗体、下划线、删除线等修饰性属性。
  Paint类与文本相关的方法如下:

//设置文本大小,单位是px,这个和我们平时使用的字体大小单位sp不同,所以最好进行转换;
public void setTextSize(float textSize)

//设置文本对齐方式,可选值有Paint.Align.LEFT、Paint.Align.CENTER、Paint.Align.RIGHT等;
public void setTextAlign(Align align)

//设置文本的倾斜程度,参数skewx取值在0~1之间,正负表示倾斜的方向;
public void setTextSkewX(float skewX)

//是否给文本添加下划线,参数underline为ture表示添加;
public void setUnderlineText(boolean underlineText)

//是否设置文本的粗体样式,参数bold为true表示设置粗体;
public void setFakeBoldText(boolean fakeBoldText)

//是否给文本添加删除线,参数strike为true表示添加;
public void setStrikeThruText(boolean strikeThruText)

  图形样式包含绘制的图形是空心样式还是实心样式,同时还能指定落笔和收笔时的笔触效果。绘制直线和折线时,图形样式能影响到一些绘制细节。
  Paint类与图形样式相关的方法如下:

//设置绘制的图形是空心样式还是实心样式,默认为实心样式。参数style的可选值有FILL、FILL_AND_STROKE、STROKE。其中,FILL表示实心样式,对于闭合图形来说,会用指定的颜色进行填充;STROKE表示空心样式,绘制时只有线条而无填充效果;FILL_AND_STROKE表示同时使用实心样式和空心样式;
public void setStyle(Style style)
//当绘图样式为STROKE时,该方法用于指定线条连接处的拐角样式,能使绘制的图形更加平滑。参数join的可选值有BEVEL、MITER、ROUND。;
public void setStrokeJoin(Join join)

//设置落笔时的样式,控制我们的画笔在离开画板时留下的最后一点图形,参数cap的可选值有:BUTT、ROUND、SQUARE。;
public void setStrokeCap(Cap cap)

//设置线条的宽度,注意是float类型,在Android中最细的线条不是1,可以比1更小更细;
public void setStrokeWidth(float width)

Canvas类

  Canvas类封装了大量的绘图方法,绘制的图形受到Paint对象的影响。一般情况下,在绘图之前,要先创建Paint对象,定义绘制的颜色、样式。Paint对象是一个轻量级对象,在程序运行过程中可以创建多个。Paint类有一个reset()方法,可以重置Paint参数。所以,除非有必要,否则我们不推荐大量创建Paint对象,而是调用reset()方法重置后重复使用。

  Canvas类定义的绘图方法主要分成以下类型:
1 . 位图;
2 . 点;
3 . 线;
4 . 矩形;
5 . 圆;
6 . 路径;
7 . 文字;

绘制位图

  Canvas中定义的绘制位图的方法如下:

//该方法最简单,将bitmap绘制在画布上,同时指定位图左上角相对于画布的坐标,大小与原位置相同,不进行任何缩放。绘制位图时,除非需要进行位图运算,否则,并不需要指定paint对象,直接传递null即可;
public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint)

//这两个方法是从bitmap中抠出一块大小区域为src的图片并绘制到canvas的dst位置,src和dst的大小与比例关系影响到最终的绘制效果;
public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst,@Nullable Paint paint)
public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,@Nullable Paint paint)

绘制点

  点的大小取决于setStrokeWidth()方法的参数,参数值越大,点也就越大。所以,不要以为一个点就是屏幕上的一个像素。如果将stroke的宽度设置为足够大,可以发现最终绘制出来的点是一个正方形。

  绘制点的方法有三个,如下:

//该方法在(x,y)处绘制一个点;
public void drawPoint(float x, float y, @NonNull Paint paint)

//该方法的参数pts是一个数组,从下标0开始每2个数确定一个点,连续绘制多个点。多余的元素会忽略;
public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint)

//该方法的参数pts是一个数组,从pts数组中的第offset处开始取出count个数字,以2个数为一组确定一个点,连续绘制若干个点。忽略多余的元素;
public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,@NonNull Paint paint)

绘制线

  两个点确定一条直线,所以,绘制线条时,需要指定两个点的坐标。同画点一样,绘制线条也有三个重载的方法,如下:

//在(startX,startY)和(stopX,stopY)两个点之间绘制一条直线;
public void drawLine(float startX, float startY, float stopX, float stopY,@NonNull Paint paint)

//在参数pts数组中每四个数作为一组绘制一条直线,多余的元素会忽略;
public void drawLines(@Size(multiple = 4) @NonNull float[] pts,@NonNull Paint paint)

//从参数pts数组中的offset索引处开始,取出count个元素,并以四个数作为一组绘制直线,忽略多余的元素;
public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count,@NonNull Paint paint)

绘制矩形

  矩形包含直角矩形和圆角矩形,正方形也是矩形的一种,所以Canvas并没有提供绘制正方形的方法。绘制矩形时,参数分为两种:一种是指定left、top、right、bottom这四个参数,另一种是直接指定一个Rect对象或RectF对象。

  绘制直角矩形的三个重载方法如下:

public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint)
public void drawRect(@NonNull Rect r, @NonNull Paint paint)
public void drawRect(@NonNull RectF rect, @NonNull Paint paint)

  圆角矩形的几何形状比直角矩形相对复杂一些,需要指定四个拐角的弧度,四个角的弧度不能单独设置,而是统一设置为相同的值。拐角弧度实际上是圆或椭圆的一段弧线。
  绘制圆角矩形有两个重载的方法,如下:

//该方法用于绘制一个圆角矩形,left、top、right、bottom构建一个矩形,rx、ry分别是圆角处的水平半径和垂直半径。rx和ry不一定相同,如果不同,则是椭圆上的一段弧线;
public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,@NonNull Paint paint)

//rect构建一个矩形,其他参数则是同上个方法一样;
public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint)

绘制圆

  我们把圆、椭圆、扇形、弧线统一归类到圆这一类中。扇形和弧线可以认为是圆或椭圆的一部分。

  椭圆的大小是由它的外切矩形来决定的。绘制椭圆的方法如下:

//left、top、right、bottom构建一个矩形,绘制的结果就是该矩形的内切椭圆;
public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint)

//rect构建一个矩形,绘制的结果就是该矩形的内切椭圆;
public void drawOval(@NonNull RectF oval, @NonNull Paint paint)

  绘制椭圆时,如果外切矩形的长和宽相等,即为正方形,绘制出来的图形就是圆,但是Canvas类提供了一个更加简单的方法,提供圆点的坐标和半径即可。

//(cx,cy)是圆心坐标,radius是圆的半径;
public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint)

  弧线是椭圆上的一段,扇形则是将弧线的两个端点和椭圆中心点使用线条连接形成的闭合区域。
  绘制弧线和扇形的方法,如下:

//left、top、right、bottom构建一个矩形,参数startAngle表示起始角度,sweepAngle表示扇形或弧线所占的角度,正数表示顺时针,负数表示逆时针,useCenter表示是否要使用中心点,useCenter为true表示扇形,为false表示弧线;
public void drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter, @NonNull Paint paint)

//oval构建一个矩形,其他参数同上面的方法;
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,@NonNull Paint paint)

绘制路径

  Path表示路径,路径可以是直的,也可以是弯的,可以是闭合的,也可以是非闭合的,可以是圆形的,也可以是方形的,可以是单个的,也可以是多个的。
  Path类用于绘制复杂图形,创建之初什么也没有,只有往Path中添加了具体的形状,Path才会清晰可见。绘制Path时,所有信息都存储在Path对象中,Canvas根据Path对象来绘制相应的图形。

  我们将Path的功能归纳以下几类:
1 . 往Path中添加线条;
2 . 往Path中添加矩形、椭圆、弧;
3 . 往Path中添加曲线和贝塞尔曲线;
4 . 将Path中的图形进行运算;

  通过Path可以绘制出奇形怪状的线条,并能将线条组合在一起变成折线,闭合后就是一个多边形了。为此,Path类中定义了5个方法,如下:

//将画笔移动到点(x,y)的位置,使用的是绝对定位;
public void moveTo(float x, float y)

//将画笔移动到一个新的点,新点在上一个点的基础上偏移(dx,dy),也就是说,新点的坐标是(x + dx,y + dy)。这里使用的是相对定位。;
public void rMoveTo(float dx, float dy)

//将画笔移动到点(x,y)的位置,并在上一个点与当前点之前画一条直线。使用的是绝对定位;
public void lineTo(float x, float y)

//将画笔移动到一个新点,新点在上一个点的基础上偏移(dx,dy),新点的坐标为(x + dx,y + dy),同时,在新点与上一个点之间画一条直线。这里使用的是相对定位;
public void rLineTo(float dx, float dy)

//在第一个点和最后一个点之前画一条直线,形成闭合区域;
public void close()

  如果要往Path对象中添加矩形、椭圆、圆和弧,需要调用Path类中定义的一组以add开头的方法,这组方法有些需要传递一个类行为Path.Direction的参数,这是一个枚举类型,枚举值CW表示顺时针,CCW表示逆时针。

//往Path对象中添加一个矩形;
public void addRect(float left, float top, float right, float bottom, Direction dir)
public void addRect(RectF rect, Direction dir)

//往Path对象中添加一个圆角矩形;
public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry,Direction dir)
public void addRoundRect(float left, float top, float right, float bottom, float[] radii,Direction dir)
public void addRoundRect(RectF rect, float rx, float ry, Direction dir)

//往Path对象中添加一个椭圆;
public void addOval(float left, float top, float right, float bottom, Direction dir)
public void addOval(RectF oval, Direction dir)

//往Path对象中添加一个圆;
public void addCircle(float x, float y, float radius, Direction dir)

//往Path对象中添加一段弧。本方法中没有指定方向,因为角度的正负已经代表了方向,正数为顺时针,负数为逆时针;
public void addArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle)
public void addArc(RectF oval, float startAngle, float sweepAngle)

  贝塞尔曲线:

//此处将来进行补充;
//此处将来进行补充;
//此处将来进行补充;

  可以将多个Path进行图形运算,得到更加复杂和不规则的图形。Path有一个静态内部类Op,定义了5种运算规则,如下:
1 . Path.Op.DIFFERENCE:
  差集,图形A减去与图形B重叠的区域后A余下的区域;

2 . Path.Op.INTERSECT:
  交集,图形A和图形B的重叠区域;

3 . Path.Op.REVERSE_DIFFERENCE:
  反差集,图形B减去与图形A重叠的区域后B余下的区域;

4 . Paht.Op.UNION:
  并集,包含了图形A和图形B的所有区域;

5 . Path.Op.XOR:
  补集,即图形A和图形B的所有区域减去他们的重叠区域后余下的区域;


绘制文字

  Canvas为我们提供了两组方法,一组是直接从指定的位置开始绘制文字,另一组是沿着Path绘制文字。

public void drawText(@NonNull char[] text, int index, int count, float x, float y,@NonNull Paint paint)
public void drawText(@NonNull CharSequence text, int start, int end, float x, float y,@NonNull Paint paint)
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint)
public void drawText(@NonNull String text, int start, int end, float x, float y,@NonNull Paint paint)

  以上方法从指定的位置开始绘制文字,提供了三种形式:char[]、CharSequence、String,本质上并没有什么不同,参数index、count、start、end可以从字符串中取出子串,而参数x、y就是文字绘制的坐标位置,其中y是文字的baseline的值。

public void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path,float hOffset, float vOffset, @NonNull Paint paint)
public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,float vOffset, @NonNull Paint paint)

  这两个方法沿着Path定义好的路径绘制文字。参数hOffset、vOffset用于定义文字离Path的水平偏移量和垂直偏移量,正数和负数影响文字与路径的相对位置。同样的,也支持绘制从字符数组中截取子串,index表示起始索引,count表示要截取的长度。

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-----------------------last line for now---------------------