Android Canvas之Path操作

简介: Android Canvas之Path操作

上一篇中介绍的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路径里赋值内容

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);

效果图:
line.png

  • 点操作
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);

效果图:
move.png

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

rmove.png

接下来看下,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);

效果图:

lastSet.jpg

虚线本来是没设置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);

效果图:
initpintu.jpg
效果很明显,设置为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);

效果图:
graph_basic.png

绘制圆弧:

//绘制圆弧
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);

效果图:
forceT.png

修改一下代码:

//在(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的最后一个点和绘制圆弧的起点,效果图:
C(~15Y{R1_(GHBUU($1S%CQ.png

  • 闭合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);

效果图:
notclose.png

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

close.png

可以调用close()后,连接了path的起始点和终点形成了一个封闭图形!

贝塞尔曲线内容较多,放在下一篇了!

相关文章
|
7月前
|
Java 数据库 Android开发
【专栏】Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理
【4月更文挑战第27天】本文探讨了Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理。通过案例分析展示了网络请求、图像处理和数据库操作的优化实践。同时,文章指出并发编程的挑战,如性能评估、调试及兼容性问题,并强调了多线程优化对提升应用性能的重要性。开发者应持续学习和探索新的优化策略,以适应移动应用市场的竞争需求。
201 5
|
7月前
|
Java Android开发
Android系统 获取用户最后操作时间回调实现和原理分析
Android系统 获取用户最后操作时间回调实现和原理分析
210 0
|
7月前
|
Linux Android开发
测试程序之提供ioctl函数应用操作GPIO适用于Linux/Android
测试程序之提供ioctl函数应用操作GPIO适用于Linux/Android
137 0
|
5月前
|
前端开发 API Android开发
Android自定义View之Canvas一文搞定
这篇文章介绍了Android自定义View中如何使用Canvas和Paint来绘制图形。Canvas可理解为画布,用于绘制各种形状如文字、点、线、矩形、圆角矩形、圆和弧。常见API包括`drawText()`、`drawPoint()`、`drawLine()`、`drawRect()`等。文章还提到了Canvas的保存、恢复、平移和旋转方法,通过绘制钟表盘的例子展示了如何实际应用。总结关键点:Canvas与Paint结合用于图像绘制,掌握Canvas的基本绘图函数及坐标变换操作是自定义View的关键。
89 0
Android自定义View之Canvas一文搞定
|
6月前
|
XML API 开发工具
Android Bitmap 加载与像素操作
Android Bitmap 加载与像素操作
55 2
|
5月前
|
Android开发
Android kernel 操作gpio
Android kernel 操作gpio
66 0
|
6月前
|
存储 算法 Java
Android 进阶——代码插桩必知必会&ASM7字节码操作
Android 进阶——代码插桩必知必会&ASM7字节码操作
300 0
|
7月前
|
XML Java API
Android 浅度解析:系统框架层修改,编译,推送相关操作
Android 浅度解析:系统框架层修改,编译,推送相关操作
286 0
|
数据库 Android开发 数据库管理
Android使用Room操作SQLite数据库让其变得无比高效和简洁(进一步完善用RecyclerView显示数据库中的数据)
Android使用Room操作SQLite数据库让其变得无比高效和简洁(进一步完善用RecyclerView显示数据库中的数据)
87 0
|
7月前
|
Android开发
[Android]视图的控触操作-MotionEvent
[Android]视图的控触操作-MotionEvent
70 0