由于canvas自带的drawXxx()方法能绘制的都是一些相对简单和比较规则的图形,所以当需要绘制一些比较复杂的2D图形的时候后canvas就显得力不从心了,所以就有了Path了,其主要用于绘制一些比较复杂的图形轮廓的,比如所多边形之类的,只要你计算好坐标,理论上任何2D图形都可以绘制得出来,下面大概的看下方法列表吧。
一、Path常用方法以及
方法名 | 描述 |
Path() | 构造方法,当然还有其他的 |
reset() | 重置path至初始化时状态,不保留内部数据结构(保留Filltype) |
rewind() | 重置path至初始化时状态 ,但是会保留内部数据结构以便重用 |
set(Path) | 将一个path赋值给当前的path,当前path的设置会被覆盖掉 |
setFillType(FillType) | 填充模式设置 |
isInverseFillType() | 填充模式判断 |
toggleInverseFillType() | 填充模式切换 |
isEmpty() | 判断Path是否为空,这里的空不是指null,而是只path对象是否有设置过 |
isRect(RectF) | 判断传入RectF对象是否为矩形 |
computeBounds(RectF,boolean) | 计算path的边界 |
incReserve(int) | 提醒系统,还有多少点要加入(个人并不知道有什么卵用,可能跟性能有关吧) |
moveTo(float,float) | 移动到某个点,下次操作时从该点开始(基于原点坐标系的偏移量) |
rMoveTo(float,float) | 移动到某个点,下次操作时从该点开始(基于当前点坐标系的偏移量) |
lineTo(float,float) | 绘制上一个点到参数中传入点的path(基于原点坐标系的偏移量) |
rLineTo(float,float) | 绘制上一个点到参数中传入点的path(基于当前点坐标系的偏移量) |
quadTo(float,float,float,float) | 绘制二次的贝塞尔曲线 (基于原点坐标系的偏移量) |
rQuadTo(float,float,float,float) | 绘制二次的贝塞尔曲线 (基于当前点坐标系的偏移量) |
cubicTo(float,float,float,float,float,float) | 绘制三次的贝塞尔曲线 (基于原点坐标系的偏移量) |
rCubicTo(float,float,float,float,float,float) | 绘制三次的贝塞尔曲线 (基于当前点坐标系的偏移量) |
arcTo(RectF,float,float,boolean) | |
close() | 封闭,通俗的说就是连接起点跟终点 |
addRect(RectF,Direction) | 添加矩形到当前path |
addOval(RectF,Direction) | 添加椭圆到当前path |
addCircle(float,float,float,Direction) | 添加圆到当前path |
addArc(RectF,float,float) | 添加圆弧到当前path |
addRoundRect(RectF,float,float,Direction) | 添加圆角矩形到当前path |
addPath(Path,float,float) | 添加path到当前path |
op(Path,Path,Op) | 对两个Path进行布尔值运行,也就是谁遮挡谁,或者取交集,取并集等等, |
setLastPoint(float,float) | 设置最后一个点的位置 |
transform(Matrix,Path) | 矩阵变化(这个好难,暂时不太懂) |
大概明白意思的方法就那么多了,当然还有些重载的方法,那些就不说了,自行查阅文档吧!接下来对这些方法做做试验,毕竟不亲自试过很难放心!
二、Path各个方法、方法参数基本演示
//创建画笔
paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10.0f);
paint.setAntiAlias(true);
//创建Path对象
path = new Path();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置画布背景颜色
canvas.drawColor(Color.YELLOW);
/**
* 没有调用moveTo方法,所以默认从画布坐标(0,0)开始
* 参数1:x坐标偏移量
* 参数2:y坐标偏移量
*/
path.lineTo(200,200);
/**
* 调整下一次操作的起点位置为(200,0)
*/
path.moveTo(200,0);
/**
* 从(200,0)开始画一条道(200,400)的仙
*/
path.lineTo(200,400);
canvas.drawPath(path,paint);
}
运行结果:
从上图可以看出,(200,400)这个点参考的坐标系还是(0,0)那个点,也就是说,不管我们后面调用多少次lineTo方法,都是以(0,0)为参考点的(没有移动过canvas的情况下),这点很重要,因为后面会介绍rlineTo方法,这个方法就不一样了。
2、为了更直观的区分带r开头的跟不带r开头的方法,我们接下来把1中的方法改为调用rLineTo(float,float)跟rMoveTo(float,float),看看结果会怎么样
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置画布背景颜色
canvas.drawColor(Color.YELLOW);
/**
* 没有调用moveTo方法,所以默认从画布坐标(0,0)开始
* 参数1:x坐标偏移量
* 参数2:y坐标偏移量
*/
path.rLineTo(200,200);
/**
* 调整下一次操作的起点位置为(200,0)
* 注意坐标系的变化
*/
path.rMoveTo(200,0);
/**
* 从(200,0)开始画一条道(200,400)的线
*/
path.lineTo(200,400);
canvas.drawPath(path,paint);
}
运行结果如下:
同样的参数,相差太大了,为了方便的理解,加了黑色跟绿色线条的两个坐标系,当第一次调用rLineTo的时候,是以A点为原点的坐标系为参考的,当调用rMoveTo的时候,这就跟之前的不一样了,坐标系不再是A,而是变成了A坐标系中的(200,200)也就是B,所以C点的偏移量就是以B点为原点的坐标系的偏移量,同理,当再次调用rLineTo的偏移量就变成了以C点为原点的坐标系的偏移量了!
3、接下来看看close()方法,看字面意思就知道,关闭嘛,其实就是闭合起点跟终点:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置画布背景颜色
canvas.drawColor(Color.YELLOW);
/**
* 移动起点到水平居中位置
*/
path.moveTo(getMeasuredWidth()/2,10);
/**
* 画三角形的其中两条边
*/
path.lineTo(10,getMeasuredHeight()-10);
path.rLineTo(getMeasuredWidth()-20,-10);
path.close();
canvas.drawPath(path,paint);
}
运行结果如下:
可以看到,在代码中,我们只是调用了一次lineTo跟一次rLineTo方法,也就是说只绘制了两条线,但是却得了一个三角形出来,这就时候因为调用了close方法的缘故,将启动跟终点相连了。
4、接下来演示先addRect()、addCircle()、addArc()、addOval()这几个方法,代码如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置画布背景颜色
canvas.drawColor(Color.YELLOW);
/**
* 将坐标系的原点移动到控件的中心(这个是之前整理canvas部分笔记的时候漏掉了,包括canvas的旋转)
* 这些操作能够让很多绘制省去很多繁琐的计算,后面再单独进行整理
*/
canvas.translate(getMeasuredWidth()/2,getMeasuredHeight()/2);
/**
* 下面是创建圆、矩形、圆弧、椭圆的轨迹的代码,并添加到path中,这里就不赘述了,canvas那个笔记里面已有详细说明
* 而且这里要强调的是,这里为了演示方便,所以直接在onDraw()中创建Path对象,在实际开发中请避免
*/
Path circle = new Path();
circle.addCircle(-getMeasuredHeight()/4,-getMeasuredWidth()/4,getMeasuredHeight()/4-30, Path.Direction.CW);
Path rect = new Path();
rect.addRect(30,-getMeasuredHeight()/2+30,getMeasuredWidth()/2-30,-30,Path.Direction.CW);
Path arc = new Path();
RectF rectF = new RectF(-getMeasuredWidth()/2+30,30,-30,getMeasuredHeight()/2-30);
arc.addArc(rectF,0,180);
Path oval = new Path();
RectF ovalF = new RectF(30, 100, getMeasuredWidth() / 2 - 30, getMeasuredHeight() / 2 - 100);
oval.addOval(ovalF, Path.Direction.CW);
/**
* 合并所有的path,方便一次性绘制出来
*/
circle.addPath(rect);
circle.addPath(arc);
circle.addPath(oval);
canvas.drawPath(circle,paint);
}
运行结果如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置画布背景颜色
canvas.drawColor(Color.YELLOW);
//将坐标系的原点移动到控件的中心
canvas.translate(getMeasuredWidth()/2,getMeasuredHeight()/2);
/**
* 创建矩形
* 而且这里要强调的是,这里为了演示方便,所以直接在onDraw()中创建Path对象,在实际开发中请避免
*/
Path path = new Path(); RectF rectF = new RectF(-200, -200, 200, 200); path.addRect(rectF, Path.Direction.CW); /** * 重置最后一个点的位置 */ path.setLastPoint(-50,50); canvas.drawPath(path,paint);}
运行结果如下:
Path.Direction.CW | 顺时针 |
Path.Direction.CCW | 逆时针 |
/**
* 创建矩形
*/
Path path = new Path();
RectF rectF = new RectF(-200, -200, 200, 200);
path.addRect(rectF, Path.Direction.CCW);
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置画布背景颜色
canvas.drawColor(Color.YELLOW);
//将坐标系的原点移动到控件的中心
canvas.translate(getMeasuredWidth()/2,getMeasuredHeight()/2);
//设置画笔
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(10.0f);
paint.setAntiAlias(true);
Path circle1 = new Path();
circle1.addCircle(-100,0,150, Path.Direction.CW);
Path circle2 = new Path();
//确定circle2的位置
circle2.addCircle(100,0,150, Path.Direction.CW);
circle1.op(circle2, Path.Op.DIFFERENCE);
canvas.drawPath(circle1,paint);
}
运行结果:
在上面代码中,我们画了两个实心圆的,由于我们使用了Path.Op.DIFFERENCE对两个圆进行了差异运算,所以circle2其实是已经看不到的了,上图中的空心圆只是我手动加上去方便理解而已!
下面罗列下不同模式下不同的显示(注意,实心的色块才是真正的显示,空心的是被op方法干掉的,只是为了方便观察,p图的时候加上去的!!)
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置画布背景颜色
canvas.drawColor(Color.YELLOW);
//将坐标系的原点移动到控件的中心
canvas.translate(getMeasuredWidth()/2,getMeasuredHeight()/2);
//设置创建画笔
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(10.0f);
paint.setAntiAlias(true);
//创建两个圆的path
Path path = new Path();
path.addCircle(-100, 0, 150, Path.Direction.CW);
path.addCircle(100, 0, 150, Path.Direction.CW);
//一、这是默认模式,取path所有占用的区域
//path.setFillType(Path.FillType.WINDING);
//二、取path未占用区域
//path.setFillType(Path.FillType.INVERSE_WINDING);
//三、取path所占用但不相交的区域
//path.setFillType(Path.FillType.EVEN_ODD);
//四、取path未占用区域或者已相交区域
path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
canvas.drawPath(path,paint);
}
运行结果: