Android开发 - Canvas中Path绘制路径的详解与使用
Path回顾
- Path类封装复合(多轮廓)几何路径由直线段、二次曲线和三次曲线组成。它可以用画布绘制:
canvas.drawPath(path, paint)
,填充或笔划(基于绘画的样式),或者可以用于剪裁或绘制路径上的文本。Path既是路径,路径走多了就变成一种套路,只要我们会解套,那这种套路就是高速公路。路径走完形成闭环,最终形成一个图形
paint回顾
-
path的合作伙伴是Paint,path设置好路径而绘制由paint完成绘制,paint既是画笔。以下是基本的画笔设置:
paint.setAntiAlias(true); // 设置画笔的风格style (Paint.Style.FILL填充,Paint.Style.STROKE描边 Paint.Style.FILL_AND_STROKE填充加描边) paint.setStyle(Paint.Style.STROKE); // 设置画笔的颜色 paint.setColor(Color.RED); //设置描边宽度 paint.setStrokeWidth(10f);
关于setStyle注意点
-
不同的填充效果不一样,比如绘制路径,三个点描边出来的效果却是两条线,而填充模式下是一个三角形的色块
-
paint.setStyle(Paint.Style.STROKE);
-
paint.setStyle(Paint.Style.FILL);
-
-
Paint.Style.FILL_AND_STROKE;
:会出现角有缺陷,在使用需要注意
-
Path的使用
构造path对象
构造空对象
public void path()
拷贝一个对象
public void path(Path src)
Path的方法
改变起始坐标:moveTo
-
moveTo(x,y);
- 参数解析:
- x:设置新的x轴坐标,默认为0
- y:设置新的y轴坐标,默认为0
- 参数解析:
-
path默认的起始坐标是0,0;如果想改变起始坐标位置,通过moveTo来设置
private void moveTo(Path path) { path.moveTo(x, y); path.lineTo(0, 1000); path.lineTo(500, 500); }
-
图一是没设置
moveTo(0,0)
默认(0,0)坐标;图二是moveTo(100,100)
新的坐标点,起始点为A,因此只变更A的位置
画点成线:rLineTo、lineTo
-
rLineTo(x,y);
- 参数解析:
- x:如果没有上一个连接点,则从0开始计算放置新的x轴坐标连接点;否则从上一个(y点)连接点开始计算放置新的x轴坐标连接点
- y:以上一个连接点(x点)为起点计算放置新的y轴坐标连接点
- 参数解析:
-
lineTo(x,y);
- 参数解析:
- x:从0开始放置新的x轴坐标连接点
- y:从0开始放置新的y轴坐标连接点
- 参数解析:
-
与lineTo相同,但坐标被视为相对于最后一个此轮廓上的点。如果没有上一个点,则将
moveTo(0,0)
将自动插入 -
rLineTo与lineTo的区别:
-
rLineTo:从A点到B点,C点的坐标以B点为起点,也就是后一个点以前一个坐标为起点
private void rLineTo(Path path) { path.rLineTo(0, 1000); //A(x):0; B(y):1000 path.rLineTo(1000, 0); //c(x):1000; y:0 }
-
lineTo:起点为0,直接将A、B、C三个点按顺序在view中连起来
private void lineTo(Path path) { path.lineTo(0, 1000); //A(x):0; B(y):1000 path.lineTo(1000, 0); //c(x):1000; y:0 }
-
贝塞尔曲线:quadTo
-
quadTo(float x1, float y1, float x2, float y2);
- 参数解析:
- x1:决定曲线弯曲朝向
- y1:决定曲线弯曲高度
- x2:决定起始点的横向位置
- y2:决定起始点的纵向位置
- 参数解析:
-
注意事项:在quadTo方法调用时moveTo方法也会进行调整:
moveTo(x, y)
调整后参数解析:- x:决定终点的横向位置
- y:决定终点的纵向位置
-
代码解析:
private void quadTo(Path path) { path.moveTo(100, 0); //决定起始点位置 path.quadTo(100, 1000, 1000, 0); //决定弯曲朝向、弯曲高度、终点位置 }
贝塞尔曲线:rQuadTo
- 与quadTo基本相同,唯一的变化是moveTo的作用变为移动曲线整体位置。如果多画一个曲线,那么第二个rQuadTo画出来的曲线起始点将连接上一个曲线的终点。如果没有上一个点,则将
moveTo(0, 0)
将自动插入。
立方贝塞尔:cubicTo
-
cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)
- 参数解析:
- x1:决定曲线的弯曲朝向
- y1:决定曲线的弯曲高度
- x2:决定曲线的弯曲朝向
- y2:决定曲线的弯曲高度
- x3:决定终点的横向位置
- y3:决定终点的纵向位置
- 参数解析:
-
代码实例
private void cubicTo(Path path) { int width=getWindowSize(0)/2; int height=getWindowSize(1)/2; path.cubicTo(0, height, width, height, width, 0); }
圆弧:arcTo
-
arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
- 参数解析:
- oval:调用RectF对象决定扇形图形view大小
- startAngle:开始角度
- sweepAngle:扇形角度
- forceMoveTo:如果为true,则始终以圆弧开始新轮廓
- 参数解析:
-
代码实例:
private void arcTo(Path path) { paint.setStyle(Paint.Style.FILL); RectF rectF=new RectF(0,0,500,500); path.arcTo(rectF,startAngle,sweepAngle,true); }
关闭当前轮廓 :close
-
close()
:关闭当前轮廓。如果当前点不等于轮廓的第一个点,自动添加线段与第一个点连接。 -
没有调用
close()
之前三个点确定两条线private void close(Path path) { path.moveTo(100,100); path.lineTo(100,500); path.lineTo(500,500); }
-
调用
close()
三个点首尾相连private void close(Path path) { path.moveTo(100,100); path.lineTo(100,500); path.lineTo(500,500); path.close(); }
将闭合矩形轮廓添加到路径:addRect
-
addRect(RectF rect, Direction dir)
- 参数解析:
- rect:调用RectF对象确定矩形区域
- dir:绘制矩形轮廓的方向,
Path.DirectionCW
顺时针,Path.Direction.CCW
逆时针
- 参数解析:
-
代码实例:
private void addRect(Path path) { RectF rect=new RectF(0,0,500,500); path.addRect(rect, Path.Direction.CCW); }
绘制椭圆:addOval
-
addOval(RectF oval, Direction dir)
- 参数解析:
- oval:调用RectF对象确定椭圆区域
- dir:绘制椭圆轮廓的方向,
Path.DirectionCW
顺时针,Path.Direction.CCW
逆时针
- 参数解析:
-
代码实例:
private void addOval(Path path) { RectF rect = new RectF(0, 0, 1000, 500); //如果后2个参数相等绘制出来的图形就是圆形 path.addOval(rect, Path.Direction.CCW); }
绘制圆形:addCircle
-
addCircle(float x, float y, float radius, Direction dir)
- 参数解析:
- x:圆心的x轴坐标位置
- y:圆心的y轴坐标位置
- radius:半径
- dir:绘制圆形轮廓的方向,
Path.DirectionCW
顺时针,Path.Direction.CCW
逆时针
- 参数解析:
-
代码实例:
private void addCircle(Path path) { path.addCircle(100, 100, 100, Path.Direction.CCW); }
在矩形区域画弧度:addArc
-
addArc(RectF oval, float startAngle, float sweepAngle)
- 参数解析:
- oval:调用RectF对象确定圆弧区域
- startAngle:开始角度
- sweepAngle:扇形角度
- 参数解析:
-
代码实例:
private void addArc(Path path) { paint.setStyle(Paint.Style.STROKE); //设置画笔空心 RectF rect = new RectF(0, 0, 500, 500); //如果这个矩形是一个正方形,那么画出来的弧度是半圆形,否则是一个不规则的股弧度(1000,500就是压扁发半圆,500,1000就是瘦高的半圆) path.addArc(rect,0,180); }
private void addArc(Path path) { paint.setStyle(Paint.Style.FILL); //设置画笔实心 RectF rect = new RectF(0, 0, 500, 500); path.addArc(rect,0,180); }
在矩形中绘制一个圆形/圆角矩形:addRoundRect
-
addRoundRect(RectF rect, float rx, float ry, Direction dir)
- 参数解析:
- rect:调用RectF对象确定矩形区域
- rx:决定圆角角度的x轴坐标位置
- ry:决定圆角角度的y轴坐标位置
- dir:绘制圆形轮廓的方向,
Path.DirectionCW
顺时针,Path.Direction.CCW
逆时针
- 参数解析:
-
代码实例:
private void addRoundRect(Path path) { RectF rect = new RectF(0, 0, 1000, 500); path.addRoundRect(rect,rect.right/2,rect.bottom/2, Path.Direction.CW); }
private void addRoundRect(Path path) { RectF rect = new RectF(0, 0, 1000, 500); path.addRoundRect(rect,100,100, Path.Direction.CW); }
-
注意点:
- rx或者ry只要有一个角的坐标为0,都无法生效
- rx和ry其实可以看成各个顶点的角度百分比
在路径中添加新的路径(子路径):addPath
三种方式
addPath(Path src, Matrix matrix)
addPath(Path src)
addPath(Path src, float dx, float dy)
参数解析
-
src:可以将已存在的path路径进行拷贝
-
matrix:指定子路径在父路径中的坐标位置,通常需要先实例化matrix后进行
matrix.setTranslate(x, y);
设置好坐标后进行传入。如果没有,默认覆盖在父路径的view上 -
dx:指定子路径在父路径中的x轴坐标位置。如果没有,默认覆盖在父路径view的x轴坐标上
-
dy:指定子路径在父路径中的y轴坐标位置。如果没有,默认覆盖在父路径view的y轴坐标上
代码实例
private void addPath(Path path) {
RectF rect = new RectF(0, 0, 1000, 500);
path.addRoundRect(rect,0,0, Path.Direction.CW);
Path path1=new Path(path);
Matrix matrix=new Matrix();
matrix.setTranslate(200,200);
path.addPath(path1,100,100);
path.addPath(path1,matrix);
}
整体偏移 :offset
-
offset(x, y)
- 参数解析:
- x:需要偏移到的x轴坐标位置
- y:需要偏移到的y轴坐标位置
- 参数解析:
-
offset是针对整个path来完成偏移的,而不是某个点,与MoveTo不同,MoveTo是针对起始坐标点
-
起始坐标是(0,0),通过偏移来完成整体的下移
private void offset(Path path) { RectF rect = new RectF(0, 0, 1000, 500); path.addRoundRect(rect,0,0, Path.Direction.CW); path.offset(100,100); }
重置最后一个点的位置:setLastPoint
-
setLastPoint(x, y)
- 参数解析:
- x:设置最后一个点的x轴坐标位置
- y:设置最后一个点的y轴坐标位置
- 参数解析:
-
代码实例:
-
正常如下
private void setLastPoint(Path path) { path.lineTo(0,1000); path.lineTo(500,1000); // path.setLastPoint(500,500); }
-
setLastPoint
private void setLastPoint(Path path) { path.setLastPoint(500,500); }
-
路径坐标控制:transform
-
transform(x, y)
- 参数解析:
- x:设置x轴坐标位置
- y:设置y轴坐标位置
- 参数解析:
-
通过Matrix对path的路径进行控制和操控,比offset更灵活,只要操作Matrix即可完成当前path的变种
Matrix matrix = new Matrix(); matrix.setTranslate(0, 0); path.transform(matrix); canvas.drawPath(path, paint);
双路径区域处理:OP
-
path的op()方法是把自身和另外一条path中的区域做相应的处理
-
代码实例:
Path path1 = new Path(); path1.addCircle(150, 150, 100, Path.Direction.CW); Path path2 = new Path(); path2.addCircle(200, 200, 100, Path.Direction.CW); path1.op(path2, Path.Op.DIFFERENCE); //OP处理 canvas.drawPath(path1, mPaint);
-
传入参数有5种模式:
-
DIFFERENCE:减去Path2后Path1区域剩下的部分
-
INTERSECT:保留Path2和Path1共同的部分
-
UNION:保留Path1和Path2
-
XOR:保留Path1和Path2去除共同的部分
-
REVERSE_DIFFERENCE :减去Path1后Path2区域剩下的部分
-
计算边界:computeBounds
-
computeBounds (RectF bounds, boolean exact)
- bounds:调用RectF对象创建一个矩形区域,测量结果会放入这个矩形
- exact:是否精确测量,目前这一个参数作用已经废弃,一般写true即可
-
代码实例:
// 移动canvas,mViewWidth与mViewHeight在 onSizeChanged 方法中获得 canvas.translate(mViewWidth/2,mViewHeight/2); RectF rect1 = new RectF(); // 存放测量结果的矩形 Path path = new Path(); // 创建Path并添加一些内容 path.lineTo(100,-50); path.lineTo(100,50); path.close(); path.addCircle(-100,0,100, Path.Direction.CW); path.computeBounds(rect1,true); // 测量Path canvas.drawPath(path,mDeafultPaint); // 绘制Path mDeafultPaint.setStyle(Paint.Style.STROKE); mDeafultPaint.setColor(Color.RED); canvas.drawRect(rect1,mDeafultPaint); // 绘制边界
重置路径:reset、rewind
-
重置Path有两个方法,分别是
reset()
和rewind()
,两者区别主要有以下两点:- reset:保留FillType设置,不保留原有数据结构
- rewind:不保留FillType设置,保留原有数据结构
-
建议:选择权重: FillType > 数据结构,因为“FillType”影响的是显示效果,而“数据结构”影响的是重建速度。
关于Path绘制路径的常用方法总结
-
moveTo
:移动起点;移动下一次操作的起点位置 -
setLastPoint
:设置终点;重置当前path中最后一个点位置,如果在绘制之前调用,效果和moveTo相同 -
lineTo
:连接直线;添加上一个点到当前点之间的直线到Path -
close
:闭合路径;连接第一个点连接到最后一个点,形成一个闭合区域 -
addRect, addRoundRect, addOval, addCircle, addPath, addArc, arcTo
:添加内容;添加(矩形,圆角矩形,椭圆,圆,路径,圆弧) 到当前Path (注意addArc和arcTo的区别) -
isEmpty
:是否为空;判断Path是否为空 -
isRect
:是否为矩形;判断path是否是一个矩形 -
set
:替换路径;用新的路径替换到当前路径所有内容 -
offset
:偏移路径;对当前路径之前的操作进行偏移(不会影响之后的操作) -
quadTo, cubicTo
:贝塞尔曲线;分别为二次和三次贝塞尔曲线的方法 -
rMoveTo, rLineTo, rQuadTo, rCubicTo
:rXxx方法;不带r的方法是基于原点的坐标系(偏移量), rXxx方法是基于当前点坐标系(偏移量) -
setFillType, getFillType, isInverseFillType, toggleInverseFillType
:填充模式;设置填充模式、获取填充模式、判断和切换填充模式 -
incReserve
查看案例:提示方法;提示Path还有多少个点等待加入(这个方法貌似会让Path优化存储结构) -
op
:布尔操作(API19);对两个Path进行布尔运算(区域分割交集处理) -
computeBounds
:计算边界;计算Path的边界 -
reset, rewind
:重置路径;清除Path中的内容,reset不保留内部数据结构,但会保留FillType;rewind会保留内部的数据结构,但不保留FillType -
transform
:矩阵操作;矩阵变换坐标位置