Android Canvas之Path操作

逆水行舟用力撑,一篙松劲退千寻。这篇文章主要讲述Android Canvas之Path操作相关的知识,希望能为你提供帮助。
接上篇, Android自定义View工具: Paint& Canvas(二)

上一篇中介绍的Canvas绘制图形只能画一些常规图形( 圆, 椭圆, 矩形等) , 如果想绘制更复杂的图形, Path神器来了!
Path是什么?
Path类将多种复合路径( 多个轮廓, 如直线段、二次曲线、立方曲线) 封装在其内部的几何路径。
如何绘制Path:
通过设置Paint的Style( FILL、STROKE、FILL_AND_STROKE) , 然后调用canvas.drawPath(path, paint); Path还可以用于剪切或者在路径上绘制文本( canvas.drawTextOnPath()) 。
Path有两个构造函数:

Path() // 空的构造函数 Path(Path src) //创建一个新的路径, 并且从src路径里赋值内容


【Android Canvas之Path操作】 Path常用方法一览表:
Path常用方法 备注
线操作
lineTo、rLineTo 绘制线
点操作
moveTo、rMoveTo 改变后面操作的起始点位置
setLastPoint 改变前面操作中最后点的位置
添加常规图形
addRect 绘制矩形
addRoundRect 绘制圆角矩形
addCircle 绘制圆
addOval 绘制椭圆
addArc、arcTo 绘制圆弧
闭合path
close 如果连接Path起点和终点能形成一个闭合图形, 则会将起点和终点连接起来形成一个闭合图形
贝塞尔曲线
quadTo、rQuadTo、cubicTo、rCubicTo 贝塞尔曲线
  • 线操作
lineTo(float x, float y) //添加当前点到目标点( x, y) 构成的直线到path rLineTo(float dx, float dy) //基于当前坐标系, 即以path最后的那个点 //为坐标系原点( 0,0) , 如果前面没有path的点, 默认是屏幕左上角( 0,0) .


注: lineTo、rLineTo起始点默认是屏幕左上角的坐标系原点( 0,0) !
示例:

//设置Paint Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(10f); //设置Path Path path = new Path(); //屏幕左上角( 0,0) 到( 200,400) 画一条直线 path.lineTo(200, 400); //(200, 400)到( 400,600) 画一条直线 path.lineTo(400, 600); //以( 400,600) 为起始点( 0,0) 偏移量为( 400,600) 画一条直线, //其终点坐标实际在屏幕的位置为( 800,1200) path.rLineTo(400, 600); canvas.drawPath(path, mPaint);


效果图:

Android Canvas之Path操作

文章图片




  • 点操作
moveTo(float x, float y) //改变接下来操作的起点位置为( x,y) rMoveTo(float dx, float dy) //接下来要操作的起点位置为( x+ dx,y+ dy) setLastPoint(float dx, float dy) //改变前一步操作点的位置, 会改变前一步的操作


先来看 moveTo和rMoveTo的区别 , 示例:


//初始化Paint Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(10f); //初始化Path Path path = new Path(); //将坐标系原点从( 0,0) 移动到( 100,100) path.moveTo(100, 100); //画从( 100,100) 到( 400,400) 之间的直线 path.lineTo(400, 400); //path.rMoveTo(0, 100); //暂时注释 path.lineTo(400, 800); canvas.drawPath(path, mPaint);


效果图:

Android Canvas之Path操作

文章图片

上面代码中, 打开注释的path.rMoveTo(0, 100), 意为下一步操作起点位置由( 400,400) 变为( 400+ 0,400+ 100) 即为( 400,500) , 效果图:

Android Canvas之Path操作

文章图片



接下来看下, moveTo和setLastPoint的区别, 同样用上面的代码,加上path.setLastPoint(100, 800), 如下:


//初始化Paint Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(10f); //初始化Path Path path = new Path(); //将坐标系原点从( 0,0) 移动到( 100,100) path.moveTo(100, 100); //画从( 100,100) 到( 400,400) 之间的直线 path.lineTo(400, 400); //新加的setLastPoint path.setLastPoint(100, 800); path.lineTo(400, 800); canvas.drawPath(path, mPaint);


效果图:

Android Canvas之Path操作

文章图片




虚线本来是没设置setLastPoint之前的路径, 设置setLastPoint( 100,800) 后, 影响到了前一步lineTo( 400,400) 操作, 变成了lineTo( 100,800) , 最后结果就变成了红颜色的path路径, 可以得出结论: moveTo影响的是后面操作的起点位置, 不会影响之前的操作; 而 setLastPoint改变前一步操作最后一个点的位置, 不仅影响前一步操作, 同时也会影响后一步操作!
  • 绘制常规图形
//绘制圆 addCircle(float x, float y, float radius, Direction dir) //绘制椭圆 addOval(RectF oval, Direction dir) addOval(float left, float top, float right, float bottom, Direction dir) //绘制矩形 addRect(RectF rect, Direction dir) addRect(float left, float top, float right, float bottom, Direction dir) //绘制圆角矩形 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)


所有方法里面都有一个共同的参数Direction :
Direction 备注
Path.Direction.CCW counter-clockwise , 沿逆时针方向绘制
Path.Direction.CW clockwise , 沿顺时针方向绘制
Direction 用法示例:
//初始化Paint Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(2f); paint.setTextSize(40f); //初始化Path Path path = new Path(); //以( 600,600) 为圆心, 300为半径绘制圆 //Path.Direction.CW顺时针绘制圆 Path.Direction.CCW逆时针绘制圆 path.addCircle(600, 600, 300, Path.Direction.CW); //沿path绘制文字 canvas.drawTextOnPath(" 痛苦最好是别人的, 快乐才是自己的; 麻烦将是暂时的, 朋友总是永恒的。" , path, 0, 0, paint); canvas.drawPath(path, paint);


效果图:
Android Canvas之Path操作

文章图片




效果很明显, 设置为Path.Direction.CW时, 文字沿顺时针绘制; 设置为Path.Direction.CCW时, 文字沿逆时针绘制。
绘制常规图形示例:
//初始化Paint Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(10f); Path path = new Path(); //以( 400,200) 为圆心, 半径为100绘制圆 path.addCircle(400, 200, 100, Path.Direction.CW); //绘制椭圆 RectF rectF = new RectF(100, 350, 500, 600); //第一种方法绘制椭圆 path.addOval(rectF, Path.Direction.CW); //第二种方法绘制椭圆 path.addOval(600, 350, 1000, 600, Path.Direction.CW); //绘制矩形 RectF rect = new RectF(100, 650, 500, 900); //第一种方法绘制矩形 path.addRect(rect, Path.Direction.CW); //第一种方法绘制矩形 path.addRect(600, 650, 1000, 900, Path.Direction.CCW); //绘制圆角矩形 RectF roundRect = new RectF(100, 950, 300, 1100); //第一种方法绘制圆角矩形 path.addRoundRect(roundRect, 20, 20, Path.Direction.CW); //第二种方法绘制圆角矩形 path.addRoundRect(350, 950, 550, 1100, 10, 50, Path.Direction.CCW); //第三种方法绘制圆角矩形 //float[] radii中有8个值, 依次为左上角, 右上角, 右下角, 左下角的rx,ry RectF roundRectT = new RectF(600, 950, 800, 1100); path.addRoundRect(roundRectT, new float[]{50, 50, 50, 50, 50, 50, 0, 0}, Path.Direction.CCW); //第四种方法绘制圆角矩形 path.addRoundRect(850, 950, 1050, 1100,new float[]{0, 0, 0, 0,50, 50, 50, 50}, Path.Direction.CCW); canvas.drawPath(path, paint);


效果图:
Android Canvas之Path操作

文章图片



绘制圆弧:


//绘制圆弧 addArc(RectF oval, float startAngle, float sweepAngle) addArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle)//forceMoveTo: 是否强制将path最后一个点移动到圆弧起点, //true是强制移动, 即为不连接两个点; false则连接两个点 arcTo(RectF oval, float startAngle, float sweepAngle,boolean forceMoveTo) arcTo(RectF oval, float startAngle, float sweepAngle) arcTo(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean forceMoveTo)


addArc和arcTo都是添加圆弧到path中, 不过他们之间还是有区别的: addArc是直接添加圆弧到path中, 而arcTo会判断要绘制圆弧的起点与绘制圆弧之前path中最后的点是否是同一个点, 如果不是同一个点的话, 就会连接两个点。
示例:

//在(400, 200, 600, 400)区域内绘制一个300度的圆弧 RectF rectF = new RectF(400, 200, 600, 400); path.addArc(rectF, 0, 300); //在(400, 600, 600, 800)区域内绘制一个90度的圆弧, 并且不连接两个点 RectF rectFTo = new RectF(400, 600, 600, 800); path.arcTo(rectFTo, 0, 90, true); //等价于path.addArc(rectFTo, 0, 90); canvas.drawPath(path, paint);


效果图:

Android Canvas之Path操作

文章图片



修改一下代码:


//在(400, 200, 600, 400)区域内绘制一个300度的圆弧 RectF rectF = new RectF(400, 200, 600, 400); path.addArc(rectF, 0, 300); //在(400, 600, 600, 800)区域内绘制一个90度的圆弧, 并且连接两个点 RectF rectFTo = new RectF(400, 600, 600, 800); path.arcTo(rectFTo, 0, 90,false); //等价于path.arcTo(rectFTo, 0, 90); canvas.drawPath(path, paint);


对比发现我们只是将arcTo最后一个参数变成了false, 即连接绘制圆弧之前path的最后一个点和绘制圆弧的起点, 效果图:
Android Canvas之Path操作

文章图片





  • 闭合path
path.close();


如果path的终点和起始点不是同一个点的话, close()连接这两个点, 形成一个封闭的图形, 示例:


//初始化Paint Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(10f); //初始化Path Path path = new Path(); //将坐标原点移动到( 300,300,) path.moveTo(300, 300); //连接(300, 300)和(300, 600)成一条线 path.lineTo(300, 600); //连接(300, 600)和(600, 600)成一条线 path.lineTo(600, 600); //path.close(); 暂时注释 canvas.drawPath(path, paint);


效果图:

Android Canvas之Path操作

文章图片



修改一下代码, 将上面的path.close()打开, 效果图:

Android Canvas之Path操作

文章图片




可以调用close()后, 连接了path的起始点和终点形成了一个封闭图形!
贝塞尔曲线内容较多, 放在下一篇了!


    推荐阅读