Path的用法总结

Path的用法总结

在之前总结canvas以及paint的时候说过,绘制需要的四个组件:一个保存像素的Bitmap,一个主持绘画操作的Canvas(往Bitmap写东西),一个绘制的基本元素(例如Rect,Path,Text,Btimap),一支画笔(用来描述绘画的颜色与风格)。canvas以及paint都已经总结了,接下来就是绘制的基本元素了,Rect、Text、Bitmap都不叫简单,绘制的基本元素就总结一下Path的用法。

Path的基本用法

先来看看Path的一些方法

1、我们先来试试画直线的方法,先试试lineTo方法

mPath.lineTo(500f, 500f)
mPath.lineTo(100f, 200f)
mPath.lineTo(300f, 200f)
mPath.lineTo(100f, 500f)
mPath.close()
canvas.drawPath(mPath, mPaint)
复制代码

close方法是将路径进行闭合,可以看到lineTo默认的是从上一个点继续绘制,如果没有上一个点或者没有调用moveTo方法,则默认是View的坐标原点(0,0),而且lineTo所传参数的坐标点都是以View的坐标为基准的。下面是换成rLineTo的效果

mPath.rLineTo(500f, 500f)
mPath.rLineTo(100f, 200f)
mPath.rLineTo(300f, 200f)
mPath.rLineTo(100f, 500f)
mPath.close()
canvas.drawPath(mPath, mPaint)
复制代码

可以看到明显的区别,rLineTo的参数坐标点是以上一个点位坐标原点来计算的

2、如果不想继续上一个点继续画的话,可以使用moveTo操作

mPath.lineTo(500f, 500f)
mPath.moveTo(500f, 300f)
mPath.lineTo(100f, 200f)
mPath.close()
canvas.drawPath(mPath, mPaint)
复制代码

可以看到第二条直线并没有继续从(500,500)的点接着画,而且close方法也没有闭合path。 close方法的注释是这样解释的:关闭当前的轮廓,如果这条轮廓当前点不是开始点,就会自动添加一条直线。 moveTo方法的注释是这样的:设置下一个轮廓起点的位置。 其实从两个方法的注释可以看出,一个Path应该是多个contour(轮廓)的组合,一个contour是一条不间断的连线,调用moveTo方法的时候会打断上一个contour,close方法会闭合一个没有闭合的contour。

3、还有一个rMoveTo的方法,其实跟moveTo差不多,都是设置下一个contour的开始点,不过r开头的是以上一个contour的结束点作为坐标原点来计算点的位置。Path里面有好几在其他方法前面加r的方法,其实都是差不多的意思,没有加r的是以View的坐标系作为绝对坐标,而r开头的是以上一个点作为相对坐标。

4、贝瑟尔曲线方法,quadTo,cubicTo,rQuadTo,rCubicTo

mPath.moveTo(point1.x, point1.y)
mPath.quadTo(controlPoint1.x, controlPoint1.y, point2.x, point2.y)
mPath.quadTo(controlPoint2.x, controlPoint2.y, point3.x, point3.y)
canvas.drawPath(mPath, mPaint)
复制代码

quadTo前两个参数是控制点的坐标,后两个参数是曲线结束点的坐标,因为quadTo是二阶曲线,只需要一个控制点,而cubicTo是三阶曲线,需要两个控制点

mPath.moveTo(point1.x, point1.y)
mPath.cubicTo(controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, point2.x, point2.y)
mPath.cubicTo(controlPoint3.x, controlPoint3.y, controlPoint4.x, controlPoint4.y, point3.x, point3.y)
canvas.drawPath(mPath, mPaint)
复制代码

5、添加图形的方法

mPath.addCircle(300f, 300f, 100f, Path.Direction.CW)
mPath.addOval(RectF(500f, 50f, 800f, 300f), Path.Direction.CW)
mPath.addRect(RectF(50f, 500f, 500f, 800f), Path.Direction.CW)
mPath.addArc(RectF(600f, 500f, 1200f, 800f), 0f, 270f)
canvas.drawPath(mPath, mPaint)
复制代码

可以看到封闭的contour都需要传一个Direction的参数,可以选择顺时针方向或者逆时针方向,它只是在需要填充图形,并且图形出现自相交时,用于判断填充范围的。怎样填充需要根据fillType来决定。还有一个方法arcTo跟addArc是有区别的

mPath.lineTo(200f, 300f)
mPath.arcTo(RectF(500f, 300f, 1200f, 600f), 0f, 120f, true)
canvas.drawPath(mPath, mPaint)
复制代码

arcTo比addArc多了一个参数forceMoveTo,当为true的时候跟addArc是一样的,但是为false的时候,会自动连接圆弧的起点,并不是开始一个新的contour。

Path的填充

Path通过setFillType(FillType ft)来设置填充类型,FillType有四种类型,WINDING、EVEN_ODD、INVERSE_WINDING、INVERSE_EVEN_ODD,我们看一下这四种type的注释:

  • WINDING:确定图形内部,非零环绕数原则
  • EVEN_ODD:确定图形内部,奇偶原则
  • INVERSE_WINDING:跟WINDING一样,但是是画图形的外部,而不是内部
  • INVERSE_EVEN_ODD:跟EVEN_ODD一样,但是是画图形的外部,而不是内部

非零环绕数原则):首先,它需要你图形中的所有线条都是有绘制方向的,然后,同样是从平面中的点向任意方向射出一条射线,但计算规则不一样:以 0 为初始值,对于射线和图形的所有交点,遇到每个顺时针的交点(图形从射线的左边向右穿过)把结果加 1,遇到每个逆时针的交点(图形从射线的右边向左穿过)把结果减 1,最终把所有的交点都算上,得到的结果如果不是 0,则认为这个点在图形内部,是要被涂色的区域;如果是 0,则认为这个点在图形外部,是不被涂色的区域

奇偶原则:对于平面中的任意一点,向任意方向射出一条射线,这条射线和图形相交的次数(相交才算,相切不算哦)如果是奇数,则这个点被认为在图形内部,是要被涂色的区域;如果是偶数,则这个点被认为在图形外部,是不被涂色的区域。

mPath.addCircle(300f, 300f, 200f, Path.Direction.CW)
mPath.addCircle(300f, 300f, 150f, Path.Direction.CCW)
mPath.addCircle(600f, 300f, 200f, Path.Direction.CW)
mPath.fillType = Path.FillType.WINDING
canvas.drawPath(mPath, mPaint)
复制代码

把fillType设置为INVERSE_WINDING
把fillType设置为EVEN_ODD
把fillType设置为INVERSE_EVEN_ODD

Path的布尔操作

Path可以通过op方法来对两个Path进行布尔运算(即交集并集),op有一下几种类型:

  • DIFFERENCE:差集操作,从第一个Path里减去第二个Path
  • INTERSECT:交集操作
  • UNION:并集操作
  • XOR:异或操作,包含Path1与Path2但不包括两者相交的部分
  • REVERSE_DIFFERENCE:与DIFFERENCE相反,从第二个Path里减去第一个Path

示例

 mPath.addCircle(300f, 300f, 200f, Path.Direction.CW)
 val path = Path()
 path.addCircle(600f, 300f, 200f, Path.Direction.CW)
 mPath.op(mPath, path, Path.Op.INTERSECT)
 canvas.drawPath(mPath, mPaint)
复制代码

DIFFERENCE:
INTERSECT:
UNION:
XOR:需要填充才能看效果

Path的辅助操作

  • isEmpty可以用来判断Path是否为空(没有包含任何的直线和曲线)
  • isRect判断path是否是一个矩形,如果是矩形,会把矩形信息写进传入的Rect参数
  • computeBounds计算Path的边界
  • reset清除Path中的内容,但会保留FillType
  • rewind清除Path中的内容,会保留内部的数据结构,但不保留FillType

Path的偏移与变换

1、可以通过offset方法来对path进行偏移

mPath.addCircle(300f, 300f, 200f, Path.Direction.CW)
canvas.drawPath(mPath, mPaint)
/**
 * @param dx x方向的偏移量
 * @param dy y方向的偏移量
 */
mPath.offset(500f, 100f)
canvas.drawPath(mPath, mPaint)
复制代码

2、可以通过transform(Matrix matrix)方法通过matrix对path进行变换,跟canvas的matrix变换类似

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值