一、Path认识
二、Path常用方法详解
第1组: moveTo、 setLastPoint、 lineTo 和 close
由于Path的有些知识点无法单独来讲,所以本次采取了一次讲一组方法。
画笔创建如下:
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - 描边
mPaint.setStrokeWidth(10); // 边框宽度 - 10
lineTo:
方法预览:
public void lineTo (float x, float y)
连线至目标点,默认第一个点位坐标原点,如果已经有点则是从上次点连线至目标点。
示例代码:
canvas.translate(mWidth / 2, mHeight / 2); // 移动坐标系到屏幕中心(宽高数据在onSizeChanged中获取)
Path path = new Path(); // 创建Path
path.lineTo(200, 200); // lineTo
path.lineTo(200,0);
canvas.drawPath(path, mPaint); // 绘制Path
在示例中我们调用了两次lineTo,第一次由于之前没有过操作,所以默认点就是坐标原点O,结果就是坐标原点O到A(200,200)之间连直线(用蓝色圈1标注)。
第二次lineTo的时候,由于上次的结束位置是A(200,200),所以就是A(200,200)到B(200,0)之间的连线(用蓝色圈2标注)。
moveTo 和 setLastPoint:
方法预览
public void moveTo (float x, float y)
public void setLastPoint (float dx, float dy)
这两个方法虽然在作用上有相似之处,但实际上却是完全不同的两个东东,具体参照下表:
方法名 | 简介 | 是否影响之前的操作 | 是否影响之后操作 |
---|---|---|---|
moveTo | 移动下一次操作的起点位置 | 否 | 是 |
setLastPoint | 设置之前操作的最后一个点位置 | 是 | 是 |
示例代码:
canvas.translate(mWidth / 2, mHeight / 2); // 移动坐标系到屏幕中心
Path path = new Path(); // 创建Path
path.lineTo(200, 200); // lineTo
path.moveTo(200,100); // moveTo
path.lineTo(200,0); // lineTo
canvas.drawPath(path, mPaint); // 绘制Path
moveTo只改变下次操作的起点,在执行完第一次LineTo的时候,本来的默认点位置是A(200,200),但是moveTo将其改变成为了C(200,100),所以在第二次调用lineTo的时候就是连接C(200,100) 到 B(200,0) 之间的直线(用蓝色圈2标注)。
下面是setLastPoint的示例:
canvas.translate(mWidth / 2, mHeight / 2); // 移动坐标系到屏幕中心
Path path = new Path(); // 创建Path
path.lineTo(200, 200); // lineTo
path.setLastPoint(200,100); // setLastPoint
path.lineTo(200,0); // lineTo
canvas.drawPath(path, mPaint); // 绘制Path
setLastPoint是重置上一次操作的最后一个点,在执行完第一次的lineTo的时候,最后一个点是A(200,200),而setLastPoint更改最后一个点为C(200,100),所以在实际执行的时候,第一次的lineTo就不是从原点O到A(200,200)的连线了,而变成了从原点O到C(200,100)之间的连线了。
在执行完第一次lineTo和setLastPoint后,最后一个点的位置是C(200,100),所以在第二次调用lineTo的时候就是C(200,100) 到 B(200,0) 之间的连线(用蓝色圈2标注)。
close
方法预览:
public void close ()
close方法用于连接当前最后一个点和最初的一个点(如果两个点不重合的话),最终形成一个封闭的图形。
示例代码:
canvas.translate(mWidth / 2, mHeight / 2); // 移动坐标系到屏幕中心
Path path = new Path(); // 创建Path
path.lineTo(200, 200); // lineTo
path.lineTo(200,0); // lineTo
path.close(); // close
canvas.drawPath(path, mPaint); // 绘制Path
很明显,两个lineTo分别代表第1和第2条线,而close在此处的作用就算连接了B(200,0)点和原点O之间的第3条线,使之形成一个封闭的图形。
注意:close的作用是封闭路径,与连接当前最后一个点和第一个点并不等价。如果连接了最后一个点和第一个点仍然无法形成封闭图形,则close什么 也不做。
第2组: addXxx与arcTo
第一类(基本形状)
方法预览:
// 圆形
public void addCircle (float x, float y, float radius, Path.Direction dir)
// 椭圆
public void addOval (RectF oval, Path.Direction dir)
// 矩形
public void addRect (float left, float top, float right, float bottom, Path.Direction dir)
public void addRect (RectF rect, Path.Direction dir)
// 圆角矩形
public void addRoundRect (RectF rect, float[] radii, Path.Direction dir)
public void addRoundRect (RectF rect, float rx, float ry, Path.Direction dir)
这一类就是在path中添加一个基本形状,基本形状部分和前面所讲的绘制基本形状并无太大差别,本次只将其中不同的部分摘出来详细讲解一下。
这里说下Path.Direction这个参数
Direction是一个枚举(Enum)类型,里面只有两个枚举常量,如下:
类型 | 解释 | 翻译 |
---|---|---|
CW | clockwise | 顺时针 |
CCW | counter-clockwise | 逆时针 |
先偷偷剧透一下这个顺时针和逆时针的作用。
序号 | 作用 |
---|---|
1 | 在添加图形时确定闭合顺序(各个点的记录顺序) |
2 | 对图形的渲染结果有影响(是判断图形渲染的重要条件) |
这个先剧透这么多,至于对闭合顺序有啥影响,图形的渲染等问题等请慢慢看下去
咱们先研究确定闭合顺序的问题,添加一个矩形试试看:
canvas.translate(mWidth / 2, mHeight / 2); // 移动坐标系到屏幕中心
Path path = new Path();
path.addRect(-200,-200,200,200, Path.Direction.CW);
canvas.drawPath(path,mPaint);
将上面代码的CW改为CCW再运行一次。接下来就是见证奇迹的时刻,两次运行结果一模一样,有木有很神奇!
其实啊,这个东东是自带隐身技能的,想要让它现出原形,就要用到咱们刚刚学到的setLastPoint(重置当前最后一个点的位置)。
canvas.translate(mWidth / 2, mHeight / 2); // 移动坐标系到屏幕中心
Path path = new Path();
path.addRect(-200,-200,200,200, Path.Direction.CW);
path.setLastPoint(-300,300); // <-- 重置最后一个点的位置
canvas.drawPath(path,mPaint);
可以明显看到,图形发生了奇怪的变化。为何会如此呢?
对于上面这个矩形来说,我们采用的是顺时针(CW),所以记录的点的顺序就是 A -> B -> C -> D. 最后一个点就是D,我们这里使用setLastPoint改变最后一个点的位置实际上是改变了D的位置。
理解了上面的原理之后,设想如果我们将顺时针改为逆时针(CCW),则记录点的顺序应该就是 A -> D -> C -> B, 再使用setLastPoint则改变的是B的位置,我们试试看结果和我们的猜想是否一致:
canvas.translate(mWidth / 2, mHeight / 2); // 移动坐标系到屏幕中心
Path path = new Path();
path.addRect(-200,-200,200,200, Path.Direction.CCW);
path.setLastPoint(-300,300); // <-- 重置最后一个点的位置
canvas.drawPath(path,mPaint);
果不其然!
关于顺时针和逆时针对图形填充结果的影响见Path完结篇
第二类(Path)
方法预览:
public void addPath (Path src)
public void addPath (Path src, float dx, float dy)
public void addPath (Path src, Matrix matrix)
这个相对比较简单,也很容易理解,就是将两个Path合并成为一个。
第二个方法比第一个方法多出来的两个参数是将src进行了位移之后再添加进当前path中。
第三个方法是将src添加到当前path之前先使用Matrix进行变换。
代码示例:
canvas.translate(mWidth / 2, mHeight / 2); // 移动坐标系到屏幕中心
canvas.scale(1,-1); // <-- 注意 翻转y坐标轴
Path path = new Path();
Path src = new Path();
path.addRect(-200,-200,200,200, Path.Direction.CW);
src.addCircle(0,0,100, Path.Direction.CW);
path.addPath(src,0,200); // 添加时进行平移操作
canvas.drawPath(path,mPaint);
效果如下:
第三类(addArc与arcTo)
方法预览:
// addArc
public void addArc (RectF oval, float startAngle, float sweepAngle)
// arcTo
public void arcTo (RectF oval, float startAngle, float sweepAngle)
public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
这两类方法作用都是添加一个圆弧到path中,但既然存在两个方法,两者之间肯定是有区别的
名称 | 作用 | 区别 |
---|---|---|
addArc | 添加一个圆弧到path | 直接添加一个圆弧到path中 |
arcTo | 添加一个圆弧到path | 添加一个圆弧到path,如果圆弧的起点和上次最后一个坐标点不相同,就连接两个点 |
方法参数表:
参数 | 摘要 |
---|---|
oval | 圆弧的外切矩形。 |
startAngle | 开始角度 |
sweepAngle | 扫过角度(-360 <= sweepAngle <360) |
forceMoveTo | 是否强制使用MoveTo |
forceMoveTo是什么作用呢?
这个变量意思为“是否强制使用moveTo”,也就是说,是否使用moveTo将变量移动到圆弧的起点位移,也就意味着:
forceMoveTo | 含义 | 等价方法 |
---|---|---|
true | 将最后一个点移动到圆弧起点,即不连接最后一个点与圆弧起点 | public void addArc (RectF oval, float startAngle, float sweepAngle) |
false | 不移动,而是连接最后一个点与圆弧起点 | public void arcTo (RectF oval, float startAngle, float sweepAngle) |
示例(addArc):
canvas.translate(mWidth / 2, mHeight / 2); // 移动坐标系到屏幕中心
canvas.scale(1,-1); // <-- 注意 翻转y坐标轴
Path path = new Path();
path.lineTo(100,100);
RectF oval = new RectF(0,0,300,300);
path.addArc(oval,0,270);
// path.arcTo(oval,0,270,true); // <-- 和上面一句作用等价
canvas.drawPath(path,mPaint);
示例(arcTo):
canvas.translate(mWidth / 2, mHeight / 2); // 移动坐标系到屏幕中心
canvas.scale(1,-1); // <-- 注意 翻转y坐标轴
Path path = new Path();
path.lineTo(100,100);
RectF oval = new RectF(0,0,300,300);
path.arcTo(oval,0,270);
// path.arcTo(oval,0,270,false); // <-- 和上面一句作用等价
canvas.drawPath(path,mPaint);
第3组:isEmpty、 isRect、isConvex、 set 和 offset
isEmpty
方法预览:
public boolean isEmpty ()
判断path中是否包含内容。
Path path = new Path();
Log.i("breeze", "onDraw: " + path.isEmpty());
path.lineTo(100, 100);
Log.i("breeze", "onDraw: " + path.isEmpty());
输出结果
I/breeze: onDraw: true
I/breeze: onDraw: false
isRect
方法预览:
public boolean isRect (RectF rect)
判断path是否是一个矩形,如果是一个矩形的话,会将矩形的信息存放进参数rect中。
set
方法预览:
public void set (Path src)
将新的path赋值到现有path,现有path原有内容会清除。
offset
方法预览:
public void offset (float dx, float dy)
public void offset (float dx, float dy, Path dst)
作用很简单,就是对path进行一段平移,它和Canvas中的translate作用很像,但Canvas作用于整个画布,而path的offset只作用于当前path。
但是第二个方法最后怎么会有一个path作为参数?
其实第二个方法中最后的参数dst是存储平移后的path的,不影响当前的path。
代码示例:
canvas.translate(mWidth / 2, mHeight / 2); // 移动坐标系到屏幕中心
canvas.scale(1,-1); // <-- 注意 翻转y坐标轴
Path path = new Path(); // path中添加一个圆形(圆心在坐标原点)
path.addCircle(0,0,100, Path.Direction.CW);
Path dst = new Path(); // dst中添加一个矩形
dst.addRect(-200,-200,200,200, Path.Direction.CW);
path.offset(300,0,dst); // 平移
canvas.drawPath(path,mPaint); // 绘制path
mPaint.setColor(Color.BLUE); // 更改画笔颜色
canvas.drawPath(dst,mPaint); // 绘制dst
从运行效果图可以看出,虽然我们在dst中添加了一个矩形,但是并没有表现出来,所以,当dst中存在内容时,dst中原有的内容会被清空,而存放平移后的path。
网友评论