Path是一个类,表示“路径”.
路径:就是无数个点连接起来形成的轨迹,这个路径中还包括起点信息、终点信息,方向信息
Canvas中绘制路径Path的方法:
public void drawPath(Path path, Paint paint)
一、Path方法大全
这里先给出Path中常用方法,现在看不懂没关系,在后面都会一一介绍到
1、移动起点
moveTo、rMoveTo 移动画笔下一次操作的起点
2、设置终点
setLastPoint 重置当前Path中最后一个点位置,会影响之前的绘制效果
3、两点之间连接成一条线段
lineTo、rLineTo 将两点之间连接成一条线段
close 将最开始操作点、最后操作点连接起来,形成闭合路径
4、添加基本图形的路径
addRect、addRoundRect添加直角、圆角矩形
addOval、addCircle、addArc、arcTo添加椭圆、圆、弧1、弧2
5、判断
isEmpty 判断Path是否为空
isRect 判断path是否是一个矩形
6、设置路径,偏移路径,添加路径
set 为当前Path设置路径
offset 对当前路径进行偏移(不会影响之后的操作)
addPath 为当前Path添加路径
7、填充模式
setFillType 设置填充模式
getFillType 获取当前填充模式
isInverseFillType 判断是否是反向填充模式
toggleInverseFillType 原有规则与反向规则之间相互切换
8、重置
reset 保留填充模式重置path
rewind 保留数据结构重置path
9、贝塞尔曲线
quadTo、rQuadTo 二阶贝赛尔曲线
cubicTo、rCubicTo 三阶贝塞尔曲线
10、两个Path运算(差集、反差集、交集、并集、补集)
op 两个Path进行运算
11、计算边界
computeBounds 计算Path的边界信息保存在矩形RectF对象中
12、矩阵操作
transform
二、Path基础
1、移动起点
现实中画画肯定有个起始点,Path添加路径也有个起始点
public void moveTo(float x, float y)
绝对定位------将画笔移动到点(x,y)
public void rMoveTo(float dx, float dy)
r:relative
相对定位------将画笔移动到点(x+dx,y+dy),(x,y)是上一次的点
现实中作画时肯定有个开始点,这两个方法就是Path中确定开始点的
默认开始点(0,0)
现实中作画时肯定会收笔,就会出现一个终点,在Path中每次添加完路径后的终点就是下一次添加路径的起始点
2、开放路径、闭合路径
从起点开始在路径中添加线段,一直到终点停止,此时就形成了一个开放路径,若把终点和起点连接起来(闭合),或者线段最后的终点和起点重合,就会形成一个闭合路径.
3、判断一个点在图形内还是图形外
(1)奇偶规则
从点P任意方向作射线,若射线与图形交点为奇数,则点P在图形内,交点为偶数表示点P在图形外
P1、P2作出的射线与图形的交点分别为0、2,偶数,故在图形外部
P3作出的射线与图形的交点分别为1,奇数,故在图形内部
(2)非零环绕数规则
首先将多边形的边矢量化,从点P任意方向作射线,每当图形的边从射线右侧穿到射线左侧时,环绕数+1,从左到右穿过时,环绕数-1。最终环绕数和为非零,则点P为内部点,否则,点P是外部点
P1:不相交,所以环绕数为0,在图形外部
P2:矩形底边从射线右侧穿到射线左侧,环绕数+1,矩形右边从从射线左侧穿到射线右侧,环绕数-1,最终环绕数和为0,在图形内部
P3:矩形底边从射线右侧穿到射线左侧,环绕数+1,在图形内部
PS:这里的图形指的是封闭路径,通常情况下这两种判断方法结果是一样的,但是也会出现不一样的情况,在下面填充模式会介绍到
三、Path中添加线段
Path类可以添加各种形状的线条,并且能将线条组合在一起,闭合之后就成是一个多边形.
public void lineTo(float x, float y)
(x,y):直线的结束点,又是下一次绘制直线路径的开始点
在上一个点与当前新点(x,y)之间画一条线
(如果没有定义过初始点并且没有上一次操作,起始点就是坐标原点(0,0))
public void rLineTo(float dx, float dy)
(dx+x,dy+y):直线的结束点,又是下一次绘制直线路径的开始点
在上一个点与当前新点(dx+x,dy+y)之间画一条线
public void close()
在第一个点和最后一个点之间画一条线,形成闭合图形
测试:
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.BLUE);
Path path = new Path();
path.moveTo(50, 50);//起点:(50,50)
path.lineTo(250, 50);//(50,50)到(250,50)之间画一条线,(250,50)是下一次绘制的起点
path.lineTo(250, 250);//(250,50)到(250,250)之间画一条线
path.close();//闭合
canvas.drawPath(path, mPaint);
mPaint.setColor(Color.RED);
Path path2 = new Path();
path2.moveTo(300, 50);//起点:(300,50)
path2.lineTo(500, 50);//(300,50)到(500,50)之间画一条线,(500,50)是下一次绘制的起点
path2.rMoveTo(-200, 0);//将起点移动到(500-200,50+0)---(300,,50)成为下一次绘制的起点
path2.rLineTo(0, 200);//(300,50)到(300+0,50+200)之间画一条线
canvas.drawPath(path2, mPaint);
}
四、setLastPoint
Path中还有一个移动操作点位置的方法,和moveTo有区别:
public void setLastPoint(float dx, float dy)
设置Path之前操作的最后一个点位置
拿上面蓝色三角形举例:
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.BLUE);
Path path = new Path();
path.moveTo(50, 50);
path.lineTo(250, 50);
path.lineTo(250, 250);
path.setLastPoint(250,100);//将最后一个点位置设置为(250,100)
path.close();
canvas.drawPath(path, mPaint);
}
仅仅多加了一行代码:本来闭合前最后一个点应该是(250, 250),现在变成了(250,100),所以闭合时连接的是(250,100)和最初的起点(50, 50).
可见:setLastPoint会影响之前的绘图,还会把之后的绘图起点更改
而moveTo仅仅影响之后的绘图,对之前的绘图没有影响.
五、Path中添加矩形
往Path中添加矩形、椭圆、圆、弧,需要调用Path类中以“add”开头的相关方法.
关于矩形,请看Rect类、RectF类,Canvas中绘制矩形相关介绍.
1、直角矩形
public void addRect(RectF rect, Direction dir)
public void addRect(float left, float top, float right, float bottom, Direction dir)
往Path中添加一个直角矩形
Direction:Path类中枚举类,表示Path绘制的方向,可选值:
Path.Direction.CW 顺时针
Path.Direction.CCW 逆时针
方向:
path.addRect(100,100,300,200, Path.Direction.CW);//矩形ABCD
path.addRect(100,100,300,200, Path.Direction.CCW);//矩形ADCB
2、圆角矩形
public void addRoundRect(RectF rect, float rx, float ry, Direction dir)
public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry,Direction dir)
往Path中添加一个圆角矩形(4个圆角一样)
public void addRoundRect(RectF rect, float[] radii, Direction dir)
public void addRoundRect(float left, float top, float right, float bottom, float[] radii, Direction dir)
往Path中添加一个圆角矩形(4个圆角可定制)
这里的参数除了radii,之前的文章都介绍过
radii:定义4个圆角的数组,一个圆角需要2个值,总共需要8个值:
顺序:左上角,右上角,右下角,左下角
测试:
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.BLUE);
Path path = new Path();
RectF rectF = new RectF(50, 50, 600, 400);
path.addRect(rectF, Path.Direction.CW);
RectF rectF2 = new RectF(100, 100, 500, 300);
path.addRoundRect(rectF2, new float[]{10, 10, 20, 20, 0, 0, 40, 40}, Path.Direction.CW);
canvas.drawPath(path, mPaint);
}
六、添加椭圆、圆、弧
关于圆,下面这些椭圆、圆、弧的方法参数和Canvas类中绘制圆作用一样,这里就不介绍了.
1、椭圆
public void addOval(RectF oval, Direction dir)
public void addOval(float left, float top, float right, float bottom, Direction dir)
2、圆
public void addCircle(float x, float y, float radius, Direction dir)
3、弧1
public void addArc(RectF oval, float startAngle, float sweepAngle)
public void addArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle)
4、弧2
public void arcTo(RectF oval, float startAngle, float sweepAngle)
public void arcTo(RectF oval, float startAngle, float sweepAngle,boolean forceMoveTo)
public void arcTo(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean forceMoveTo)
这里多了一个参数没有介绍过:forceMoveTo
forceMoveTo表示是否强制开始一个新的起点,默认false,会在绘制弧的时候把弧的起点和Path上一次终点连接起来
测试:
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.BLUE);
Path path = new Path();
path.addOval(new RectF(50, 50, 600, 400), Path.Direction.CW);//顺时针椭圆
path.addCircle(150, 550, 100, Path.Direction.CW);//顺时针圆
path.addArc(new RectF(80, 80, 630, 430), 0, 90);//弧1
path.arcTo(new RectF(100, 100, 660, 460), 0, 90, false);//弧2
canvas.drawPath(path, mPaint);
}
七、判断
public boolean isEmpty()
判断路径是否为空,为空,返回true(Path中未添加任何线段或者曲线)
public boolean isRect(RectF rect)
判断路径是否为矩形,如果是,返回true,将矩形的信息存放进参数rect中,否则返回false,忽略rect
这两个方法很简单,就不再测试了
八、设置路径,偏移路径,添加路径
1、设置路径
public void set(Path src)
将传入的Path路径src设置给当前的Path对象
测试:
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.BLUE);
Path path1 = new Path();
Path path2 = new Path();
path1.lineTo(200, 200);
// path2.set(path1);设置路径
canvas.drawPath(path2, mPaint);
}
上面的测试代码运行界面上将没有任何效果,因为path2中未添加任何内容,但是放开注释,为path2设置路径后将会出现一条从(0,0)到(200,200)的线段.
2、偏移路径
public void offset(float dx, float dy)
对当前路径进行偏移,dx、dy分别是Path水平、竖直方向的平移距离(不会影响之后的操作)
接着上面测试代码新加一行:
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.BLUE);
Path path1 = new Path();
Path path2 = new Path();
path1.lineTo(200, 200);
path2.set(path1);//设置路径
path2.offset(300, 100);//偏移路径
canvas.drawPath(path2, mPaint);
}
3、添加路径
public void addPath(Path src)
将src的路径添加到当前Path对象中
public void addPath(Path src, float dx, float dy)
将src的路径水平、竖直方向分别偏移dx、dy距离后添加到当前Path对象中
继续上面的测试:
private void gogogo1(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.BLUE);
Path path1 = new Path();
Path path2 = new Path();
path1.lineTo(200, 200);
path2.set(path1);//设置路径
path2.offset(300, 100);//偏移路径
path2.addPath(path1);//添加路径
path2.addPath(path1, 200, 100);//添加路径
canvas.drawPath(path2, mPaint);
}
九、填充模式
public void setFillType(FillType ft)
设置填充模式
FillType 可选值:
Path.FillType.WINDING 非零环绕数规则(默认填充模式)
Path.FillType.INVERSE_WINDING 反非零环绕数规则
Path.FillType.EVEN_ODD 奇偶规则
Path.FillType.INVERSE_EVEN_ODD 反奇偶规则
填充:将图形内部填满某种颜色
Path代表路径,本文一开始介绍了如何判断一个点在图形的内部还是外部,而填充就是将Path形成的图形内部所有点都填满某种颜色,几乎所有绘图软件中都有填充功能,下面以电脑上的画图软件示例:
注意:填充针对的是图形内部
1、填充模式分为两组
根据奇偶规则判断的填充模式、根据非零环绕数规则判断的填充模式.
每组的两个模式填充效果相反.
2、为什么会分为两组
这是因为在某些情况下这两组判断的结果不一样.
对于简单的封闭路径(路径无相交的现象),图形的外部和内部和很容易判断.
但对于一些复杂的封闭路径,图形的外部和内部根据不同模式判断结果不一样.
(如下图)
根据奇偶规则:交点数为2,点P在图形外部
根据非零环绕数规则:环绕数为-2,非零,点P在图形内部
所以根据这两组模式填充的结果就不一样了
3、测试
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.BLUE);
//同向的两个圆
Path path = new Path();
path.addCircle(300, 300, 150, Path.Direction.CW);
path.addCircle(400, 300, 150, Path.Direction.CW);
path.setFillType(Path.FillType.WINDING);
// path.setFillType(Path.FillType.INVERSE_WINDING);
// path.setFillType(Path.FillType.EVEN_ODD);
// path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
canvas.drawPath(path, mPaint);
}
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.BLUE);
//反向的两个圆
Path path = new Path();
path.addCircle(300, 300, 150, Path.Direction.CW);
path.addCircle(400, 300, 150, Path.Direction.CCW);
path.setFillType(Path.FillType.WINDING);
// path.setFillType(Path.FillType.INVERSE_WINDING);
// path.setFillType(Path.FillType.EVEN_ODD);
// path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
canvas.drawPath(path, mPaint);
}
4、分析
上面测试了同向圆、反向圆形成图形的填充效果,可以看出:
(1)、Path的方向对奇偶规则没啥影响,因为不管你正向还是逆向,交点数不会改变,所以两组测试中根据奇偶规则判断的填充效果一致
(2)、Path的方向直接影响非零环绕数规则,同向时,两个圆相交区域内的点环绕数为-2,非零,所以在图形内部,反向时,两个圆相交区域内的点环绕数为0,在图形外部.
5、关于填充模式的其它几个方法
public FillType getFillType()
获取当前的填充模式
public boolean isInverseFillType()
判断当前填充模式是否是反向规则 (也就是判断是不是INVERSE_WINDING 、INVERSE_EVEN_ODD)
public void toggleInverseFillType()
切换填充规则 (即原有规则与反向规则之间相互切换)
十、重置
public void reset()
public void rewind()
都是重置Path
reset会保留填充模式 (FillType)
rewind不保留填充模式,而是保留Path内部内部数据结构