/**
* The Path class encapsulates compound (multiple contour) geometric paths consisting of straight line segments, quadratic curves, and cubic curves.
* It can be drawn with canvas.drawPath(path, paint), either filled or stroked
* (based on the paint's Style), or it can be used for clipping or to draw
* text on a path.
*/
翻译: Path封装了由直线和曲线(二次,三次贝塞尔曲线)构成的几何路径。你能用Canvas中的drawPath来把这条路径画出来(同样支持Paint的不同绘制模式),也可以用于剪裁画布和根据路径绘制文字。我们有时会用Path来描述一个图像的轮廓,所以也会称为轮廓线(轮廓线仅是Path的一种使用方法,两者并不等价)。
从注释中可以看出,Path类封装了一些复合的几何路径,其中包括直线,二次曲线,三次曲线等,以及做图表什么的都可以。我们来看一下他的源代码,其中有一个内部类Direction,在我们调用Path的一系列add方法往Path里面添加图形的时候,就会需要提供一个方向,比如我们调用addRect方法添加一个矩形Rect(100,100,200,200)的时候,假设该矩形的四个顶点为ABCD,坐标分别为A(100,100),B(200,100),C(200,200),D(200,100),在add到Paht里的时候,Path会根据指定的方向,来解析每个点构成的直线,如果指定的方向是顺时针的话,则矩形的四条边添加和绘制的顺序依次是AB,BC,CD,DA;如果是逆时针的话,则依次为AD,DC,CB,BA,下面会通过代码实例来验证我们的想法。
Direction的源码如下:
/**
* Specifies how closed shapes (e.g. rects, ovals) are oriented when they
* are added to a path.
*/
public enum Direction {
/** 顺时针方向*/
CW (0), // must match enum in SkPath.h
/** 逆时针方向 */
CCW (1); // must match enum in SkPath.h
Direction(int ni) {
nativeInt = ni;
}
final int nativeInt;
}
测试代码如下:
mPath.addRect(200, 100, 500, 300, Path.Direction.CW);
canvas.drawPath(mPath, mPaint);
mPath.reset();
mPath.addRect(200, 400, 500, 600, Path.Direction.CCW);
canvas.drawPath(mPath, mPaint);
效果图如下:
从实现的效果图中,我们发现,在绘制规则图形的时候,顺时针和逆时针基本没有差别,为了看出差异,我们修改一下上面的代码如下:
setLayerType(LAYER_TYPE_SOFTWARE, null);//需要关闭硬件加速功能,否则画出的形状是不闭合的,
mPath.addRect(100, 100, 300, 300, Path.Direction.CW);
mPath.setLastPoint(50, 200);
mPath.close();
canvas.drawPath(mPath, mPaint);
mPath.reset();
mPath.addRect(100, 400, 300, 600, Path.Direction.CCW);
mPath.setLastPoint(50, 500);
mPath.close();//使图形闭合
canvas.drawPath(mPath, mPaint);
实现效果如下:
当我们指定最后一个绘制点的时候,顺时针和逆时针就可以看出差异了。
当我们在指定的路径上画文字的时候,文字的方向就会跟我们添加的路径时所使用的方向保持一致。
如:
mPath.addCircle(400, 400, 300, Path.Direction.CW);
canvas.drawPath(mPath, mPaint);
canvas.drawTextOnPath("顺时针方向绘制文字", mPath, 25, 25, mPaint);
mPath.reset();
mPath.addCircle(400, 1100, 300, Path.Direction.CCW);
canvas.drawPath(mPath, mPaint);
canvas.drawTextOnPath("逆时针方向绘制文字", mPath, 25, 25, mPaint);
效果图如下:
上面介绍了一下Path.Direction类的基本用法,接下来就介绍Path常用Api。
Path常用Api简述:
方法名称 | 方法描述 |
---|---|
moveTo | 移动到下一次操作的起点位置 |
setLastPoint | 重置当前path中最后一个点位置,如果在绘制之前调用,效果和moveTo相同 |
lineTo | 添加上一个点到当前点之间的直线到Path中 |
close | 连接第一个点到最后一个点,使之形成一个闭合区域 |
addRect, addRoundRect, addOval, addCircle, addPath, addArc, arcTo | 添加(矩形, 圆角矩形, 椭圆, 圆, 路径, 圆弧) 到当前Path (注意addArc和arcTo的区别) |
isEmpty | 判断Path是否为空 |
isRect | 判断path是否是一个矩形 |
set | 用新的路径替换当前路径所有内容 |
offset | 对当前路径之前的操作进行偏移(不会影响之后的操作) |
quadTo, cubicTo | 分别为添加二阶和三阶贝塞尔曲线的方法 |
rMoveTo, rLineTo, rQuadTo, rCubicTo | 不带r的方法是基于原点的坐标系(偏移量), rXxx方法是基于当前点(即以当前点为坐标原点)坐标系(偏移量) |
setFillType, getFillType, isInverseFillType, toggleInverseFillType | 设置,获取,判断和切换填充模式 |
incReserve | 提示Path还有多少个点等待加入(这个方法貌似会让Path优化存储结构) |
op | 对两个Path进行布尔运算(即取交集、并集等操作) |
computeBounds | 计算Path的边界 |
reset, rewind | 清除Path中的内容,reset不保留内部数据结构,但会保留FillType;rewind会保留内部的数据结构,但不保留FillType |
transform | 矩阵变换 |
Path常用Api实例详解
在UI绘制的时候,硬件加速在某些情况下会引起一些绘制问题,如上面的绘制,在没有关闭硬件加速的时候,绘制的图形是不闭合的,所以为了避免不必要的麻烦,在绘制之前关闭硬件加速,关闭的方式有如下几种方式:
- setLayerType(LAYER_TYPE_SOFTWARE, null);在自定义控件初始化的时候设置
- 在AndroidMenifest文件中application节点下添上 android:hardwareAccelerated=”false”
Path添加圆弧的方法:addArc,arcTo
// addArc
public void addArc (RectF oval, float startAngle, float sweepAngle);
// arcTo
public void arcTo (RectF oval, float startAngle, float sweepAngle);
/**
* Append the specified arc to the path as a new contour. If the start of
* the path is different from the path's current last point, then an
* automatic lineTo() is added to connect the current contour to the
* start of the arc. However, if the path is empty, then we call moveTo()
* with the first point of the arc.
*
* @param startAngle 开始角度
* @param sweepAngle 偏移角度,即绘制多少角度的圆弧
* @param forceMoveTo true把
*/
public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
注释翻译:添加一个指定的圆弧到Path中,如果被添加的圆弧的起点与当前Path路径的最后一个点不相同,那么将会自动的调用lineTo方法,把前路径最后一个点与圆弧的起点用直线连接起来。如果当前Path为空,则调用moveTo方法,把圆弧的第一个点设置为起点。
通过注释我们可以了解到,如果forceMoveTo参数为true,那么会调用moveTo方法把被圆弧的起点设置为当前路径的最后一个点,这样当前Path路径的最后一个点的坐标与被添加圆弧的起点坐标是一样的,这样就不会调用lineTo方法去连线了,如果forceMoveTo参数为false,那么当前Path路径的最后一个点没有改变,该是啥就是啥,如果与被添加的圆弧起点不一致,则会调用lineTo方法添加一条连线,把当前Path的最后一个点与圆弧的起点连接起来。默认为false。
实例代码:
Path path = new Path();
path.lineTo(100, -100);
path.arcTo(new RectF(-300, -300, -100, -100), 0, 270);//
path.arcTo(new RectF(-50, -50, 100, 100), 0, 270, true);
canvas.drawPath(path, paint);
效果图如下:
说明:
- path.lineTo(100, -100);方法添加了一条直线1,执行完后,Path的最后一个点为A,
- path.arcTo(new RectF(-300, -300, -100, -100), 0, 270);向Path中添加了一个圆弧2,arcTo方法默认forceMoveTo参数为false,所以在执行arcTo方法的时候,发现当前Path的最后一个点是A,但被添加的圆弧2的起点B与A的坐标不一致,所以会调用lineTo方法,把A和B两点连接起来,于是就有了线条4。执行顺序:先画直线4,在画圆弧2。所以执行完该代码后,Path的最后一个点为C。
- path.arcTo(new RectF(-50, -50, 100, 100), 0, 270, true);向Path中添加一个圆弧3,此时forceMoveTo参数为true,则会先调用moveTo方法(参数为D的坐标),这时当前Path的最后一个点的就变成了D,与圆弧3的起点坐标是一样的,所以就没有在调用lineTo方法去画直线了。执行完该代码后,当前Path的最后一个点为E。
总结:addArc就是简单的添加一条圆弧,当arcTo方法方法的forceMoveTo=true的时候,与addArc方法等价。
推荐一篇写的很好的博客:安卓自定义View进阶-Path之基本操作