一.canvas.drawXXX()方法
前言:drawXXX()的方法主要是在onDraw(),dispatchDraw(),onDrawForeground()这三个方法中实现的,具体这三个方法的区别后期会讲到
Canvas.drawXXX() 和 Paint 基础
drawXXX() 系列方法和 Paint 的基础掌握了,就能够应付简单的绘制需求。它们主要包括:
Canvas 类下的所有 draw- 打头的方法,例如 drawCircle() drawBitmap()。
Paint 类的几个最常用的方法。具体是:
Paint.setStyle(Style style) 设置绘制模式–填充或者不填充
Paint.setColor(int color) 设置颜色
Paint.setStrokeWidth(float width) 设置线条宽度
Paint.setTextSize(float textSize) 设置文字大小
Paint.setAntiAlias(boolean aa) 设置抗锯齿开关
1.为当前view填充颜色:canvas.drawColor(@ColorInt int color)/canvas.drawARGB(int a,int r,int g,int b),/canvas.drawRGB(,int r,int g,int b)
举几个栗子:
canvas.drawColor(Color.BLACK);
canvas.drawColor(Color.parseColor("000000"));
canvas.drawRGB(45, 45, 45);
canvas.drawARGB(125, 45, 45, 45);
2.画圆:canvas.drawCircle(float centerX, float centerY, float radius, Paint paint)
参数:centerX:圆心x坐标/像素 ; centerY:圆心Y坐标/像素 ;radius:圆的半径 ;paint:画笔(设置圆形的样式)
这里有个Android坐标系的问题:圆点就是View的左上顶点,x轴水平向右延伸,y轴竖直向下延伸
如果要绘制其他样式的圆,比如空心圆,红色的圆等等,可以在paint中设置,常用的设置类型都在上面提到了。
举个栗子:
//在屏幕100,100的位置绘制一个半径为50的红心圆
Paint paint=new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
canvas.drawCircle(100,100,50,paint);
3.画矩形:canvas.drawRect(float left, float top, float right, float bottom, Paint paint)/canvas.drawRect(RectF rect, Paint paint),canvas.drawRect(RectF rect, Paint paint)
参数:left, top, right, bottom 是矩形四条边的坐标;paint:画笔(设置矩形的样式);rect就相当于把left,top,right,bottom封装到了一个对象中。
举个栗子:
//在屏幕100,100的位置绘制一个长为200,高为100的红色实心矩形
Paint paint=new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
canvas.drawRect(100,100,300,200,paint);
4.画点
4.1画单个点:canvas.drawPoint(float x, float y, Paint paint)
参数:x:点心x坐标;y:点心y坐标;paint:画笔(设置点的样式)
这里还有两个知识:点的大小paint.setStrokeWidth(int width);
点的形状 paint.setStrokeCap(Cap cap),这里的形状还适用于画线设置线头两端的形状
举个栗子:
//在屏幕100,100的位置绘制一个大小为10的红色的点
Paint paint=new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
//BUTT 方形(平头),ROUND 圆形(圆头), SQUARE 方形(方头)
paint.setStrokeCap(Paint.Cap.BUTT);
canvas.drawPoint(100,100,paint);
4.2画多个点:canvas.drawPoints(float[] pts, int offset, int count, Paint paint) / canvas.drawPoints(float[] pts, Paint paint)
参数:pts:点心坐标数组({0,0,1,1,2,2}两两为一组xy坐标);offset:从数组第几个数值开始绘制;count:一共绘制count个数值,绘制count/2个点。
举个栗子:
//描述困难直接上图,看下面
Paint paint=new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
paint.setStrokeCap(Paint.Cap.BUTT);
float[] pts={0, 0, 50, 50, 50, 100, 100, 50, 100, 100, 150, 50, 150, 100};
//从第一个50开始绘制,绘制到最后一个100后停止绘制
canvas.drawPoints(pts,2,8,paint);
太小了看不到,看不到,看不到
5.画椭圆:canvas.drawOval(float left, float top, float right, float bottom, Paint paint)/canvas.drawOval(RectF rect, Paint paint)
参数:left, top, right, bottom 是这个椭圆的左、上、右、下四个边界点的坐标;paint:画笔(设置矩形的样式);rectF就相当于把left,top,right,bottom封装到了一个对象中。
举个栗子:
//在屏幕100,100的位置绘制一个长为400,高为200的红色空心椭圆
Paint paint=new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
canvas.drawOval(100,100,500,300,paint);
6.画线
6.1画单个线:drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
参数:startX, startY, stopX, stopY 分别是线的起点和终点坐标;paint:画笔(设置矩形的样式);
举个栗子:
//从100,100到500,300的红色线段,这里就可以设置paint的Cap样式
Paint paint=new Paint();
paint.setColor(Color.RED);
canvas.drawLine(100,100,500,300,paint);
6.1画多个线:drawLines(float[] pts, int offset, int count, Paint paint) / drawLines(float[] pts, Paint paint)
参数:类似于绘制多个点,这里的pts可以理解为四个数值为一组,分别代表起点x,起点y,终点x,终点y
举个栗子:
//这里直接用HenCoder的数据,形状如下
Paint paint=new Paint();
paint.setColor(Color.RED);
float[] points = {20, 20, 120, 20, 70, 20, 70, 120, 20, 120, 120, 120, 150, 20, 250, 20, 150, 20, 150, 120, 250, 20, 250, 120, 150, 120, 250, 120};
canvas.drawLines(points, paint);
这么大能看到,能看到,能看到
7.画圆角矩形:canvas.drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)/canvas.drawRoundRect(RectF rect, float rx, float ry, Paint paint)
参数:left, top, right, bottom 是四条边的坐标;rx 和 ry 是圆角的横向半径和纵向半径;paint:画笔(设置圆角矩形的样式);rectF就相当于把left,top,right,bottom封装到了一个对象中。
举个栗子:
//在屏幕100,100的位置绘制一个长为400,高为200,弧度为50的红色实心圆角矩形
Paint paint=new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
canvas.drawRoundRect(100, 100, 500, 300, 50, 50, paint);
7.画弧形:canvas.drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
参数:left, top, right, bottom 分别是椭圆的四个边的坐标;startAngle:弧形开始的角度;sweepAngle:弧形扫过的角度;userCenter:弧形时候连接圆心(true:扇形;false:弧形),paint:画笔(设置弧形的样式);
举个栗子:
//在屏幕100,100的位置绘制一个长为400,高为200,弧度为50的红色实心圆角矩形
Paint paint=new Paint();
paint.setStyle(Paint.Style.FILL);
canvas.drawArc(200, 100, 800, 500, -110, 100, true, paint); // 绘制实心的扇形
canvas.drawArc(200, 100, 800, 500, 20, 90, false, paint); // 绘制实心的弧形
paint.setStyle(Paint.Style.STROKE);
canvas.drawArc(200, 100, 800, 500, 100, 160, true, paint); // 绘制封口空心的弧形弧形
canvas.drawArc(200, 100, 800, 500, 180, 60, false, paint); // 绘制不封口的空心弧形
8.画Bitmap:drawBitmap(Bitmap bitmap, float left, float top, Paint paint) /drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) / drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) / drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)
参数:left, top就是绘制图片的起点坐标;paint:画笔(设置Bitmap的样式);
举个栗子:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
canvas.drawBitmap(bitmap, 200, 100, paint);
9.绘制文字:drawText(String text, float x, float y, Paint paint)
参数:text:要绘制的文字;left, top就是绘制文字的起点坐标;paint:画笔(设置文字的样式);
举个栗子:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
canvas.drawBitmap(bitmap, 200, 100, paint);
Canvas.drawPath(Path path,Paint paint)画自定义图形
参数:path:路径(可以通过该类绘制出不同的形状);paint:画笔(…)
既然是自定义图形,那么就会遇到两个图形相交的情况,这里先补充一下相交图形的填充模式:path.setFillType(Path.FillType ft).
这里的FillType取值有四个:EVEN_ODD、WINDING、INVERSE_EVEN_ODD、INVERSE_WINDING
EVEN_ODD(奇偶原则)
对于平面中的任意一点,向任意方向射出一条射线,这条射线和图形相交的次数(相交才算,相切不算哦)如果是奇数,则这个点被认为在图形内部,是要被涂色的区域;如果是偶数,则这个点被认为在图形外部,是不被涂色的区域。
射线的方向不影响结果,如果射线经过交点,则观察有几条线经过该交点就计算几次
WINDING(非零环绕数原则)
以 0 为初始值,对于射线和图形的所有交点,遇到每个顺时针的交点(图形从射线的左边向右穿过)
把结果加1,遇到每个逆时针的交点(图形从射线的右边向左穿过)把结果减 1,
最终把所有的交点都算上,得到的结果如果不是 0,则认为这个点在图形内部,
是要被涂色的区域;如果是0,则认为这个点在图形外部,是不被涂色的区域。
*射线的方向不影响结果,如果射线经过交点,则观察有几条线经过该交点就计算几次
这里用一张图来表现几个相交图形的填充,便于理解Path的填充方式
- INVERSE_EVEN_ODD(将EVEN_ODD的效果进行反转)
- INVERSE_WINDING(将WINDING的效果进行反转)
path正文
1.Path添加子图形:path.addXXX()
- addCircle(float x, float y, float radius, Direction dir) 添加圆
和canvas.drawCircle()使用方法差不多,少了一个Paint参数,多了一个Direction参数(绘制的方向:顺时针 (CW clockwise) 和逆时针 (CCW counter-clockwise)),实际使用中不同的绘制方向就会影响到上面提到的填充模式,而paint参数会在canvas.drawPath()中使用
- addOval(float left, float top, float right, float bottom, Direction dir) / addOval(RectF oval, Direction dir) 添加椭圆
和canvas.drawOval()使用方法差不多,少了一个Paint参数,多了一个Direction参数(绘制的方向:顺时针 (CW clockwise) 和逆时针 (CCW counter-clockwise)),实际使用中不同的绘制方向就会影响到上面提到的填充模式,而paint参数会在canvas.drawPath()中使用
- addRect(float left, float top, float right, float bottom, Direction dir) / addRect(RectF rect, Direction dir) 添加矩形
和canvas.drawRect()使用方法差不多,少了一个Paint参数,多了一个Direction参数(绘制的方向:顺时针 (CW clockwise) 和逆时针 (CCW counter-clockwise)),实际使用中不同的绘制方向就会影响到上面提到的填充模式,而paint参数会在canvas.drawPath()中使用
- addRoundRect(RectF rect, float rx, float ry, Direction dir) / addRoundRect(float left, float top, float right, float bottom, float rx, float ry, Direction dir) / addRoundRect(RectF rect, float[] radii, Direction dir) / addRoundRect(float left, float top, float right, float bottom, float[] radii, Direction dir) 添加圆角矩形
和canvas.drawRoundRect()使用方法差不多,少了一个Paint参数,多了一个Direction参数(绘制的方向:顺时针 (CW clockwise) 和逆时针 (CCW counter-clockwise)),实际使用中不同的绘制方向就会影响到上面提到的填充模式,而paint参数会在canvas.drawPath()中使用
-addArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle) / addArc(RectF oval, float startAngle, float sweepAngle)添加弧形
-arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo) / arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo) / arcTo(RectF oval, float startAngle, float sweepAngle) 画弧形
先说一下addArc和arcTo的区别:addArc就是默认forceMoveTo参数为true的arcTo。而forceMoveTo的意思是上次绘制结束后是否连线到当前绘制的弧形,
这里少了userCenter和Paint参数,Path是用来绘制路径的,所以这里只会绘制弧形,而不会绘制扇形,所以用不到userCenter,Paint参数我就不说了。
2.画线:path.XXXTo()
- lineTo(float x, float y) / rLineTo(float x, float y) 画直线
从当前位置(最后一次调用path绘制后的终点位置)向目标位置画一条直线, x 和 y 是目标位置的坐标。这两个方法的区别是,lineTo(x, y) 的参数是绝对坐标,即以当前位置为起点,以x,y的位置为终点。而 rLineTo(x, y) 的参数是相对当前位置的相对坐标 (前缀 r 指的就是 relatively 「相对地」),即以当前位置为起点,以当前位置的x坐标向右增加x,从当前位置的y坐标向下增加y的点作为终点
- quadTo(float x1, float y1, float x2, float y2) / rQuadTo(float dx1, float dy1, float dx2, float dy2) 画二次贝塞尔曲线
这条二次贝塞尔曲线的起点就是当前位置,而参数中的 x1, y1 和 x2, y2 则分别是控制点和终点的坐标。和 rLineTo(x, y) 同理,rQuadTo(dx1, dy1, dx2, dy2) 的参数也是相对坐标
-cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) / rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3) 画三次贝塞尔曲线
和上面这个 quadTo() rQuadTo() 的二次贝塞尔曲线同理,cubicTo() 和 rCubicTo() 是三次贝塞尔曲线,不再解释。
arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo) / arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo) / arcTo(RectF oval, float startAngle, float sweepAngle) 画弧形
这里解释一下drawArc、arcTo、addArc三个的关系:
drawArc:画一个扇形或弧形
arcTo:画一个弧形
addArc:画一个弧形,相当于arcTo方法中的forceMoveTo参数为true的简化
-moveTo(float x, float y) / rMoveTo(float x, float y) 移动到目标位置
将绘制的起点移动到目标位置,绘制的起点默认为最后一次绘制后的位置,初始值为原点。
-close() 封闭当前子图形
将最后绘制结束点的坐标与起点坐标连起来,和lineTo(起点坐标)一个效果