1 概述
在前面的路径和文字中,讲解了path的基本用法,这里讲解一些上篇没有讲到的东西。
2 Path
这里讲解path相关的方法,后面继续讲解PathMeasure,以及实例
(1) offset
public void offset(float dx, float dy)
public void offset(float dx, float dy, Path dst)
这里两个方法都是指定offset,使得path偏移。其中第二个方法中的dst代表了移动后的path写入的目标。如果为null,则会写入调用该方法的path中。
这里使用了第一个方法,代码如下
Path path = new Path();
path.moveTo(0, 0);
path.lineTo(200, 200);
canvas.drawPath(path, paint);
path.offset(500, 0);
canvas.drawPath(path, paint);
再看看第二个方法的用法:
代码如下
Path path = new Path();
path.moveTo(0, 0);
path.lineTo(200, 200);
canvas.drawPath(path, paint);
Path path1 = new Path();
path1.moveTo(0, 0);
path1.lineTo(100, 100);
path1.offset(500, 0, path);
canvas.drawPath(path, paint);
看这两句代码
path1.offset(500, 0, path);
canvas.drawPath(path, paint);
可以看出,偏移后的路径被写入了path中。
(2) FillType
FillType,有点类似前面讲解的xfermode,不过这里比较简单,只有有四个值,如下:
WINDING//默认值,取两个图形相交
EVEN_ODD//取不相交的部分
INVERSE_WINDING//反转相交
INVERSE_EVEN_ODD//反转不相交部分
来看看实验的代码:
Path path = new Path();
path.addRect(100, 100, 500, 500, Path.Direction.CW);
path.addCircle(500, 500, 300, Path.Direction.CW);
path.setFillType(Path.FillType.WINDING);
canvas.drawPath(path, paint);
这里使用的默认的WINDING,来看看各种FillType对应的图形:
WINDING
EVEN_ODD
INVERSE_WINDING
INVERSE_EVEN_ODD
看了上面的图片,四种模式基本就清晰了。
(3) reset
reset放在FillType后面讲解,就是因为它和reset有关系,reset会清空path的所有数据,但是不会清空FillType。我们用一段代码来证实:
paint.setColor(Color.BLUE);
Path path = new Path();
path.addRect(100, 100, 500, 500, Path.Direction.CW);
path.addCircle(500, 500, 300, Path.Direction.CW);
path.setFillType(Path.FillType.INVERSE_WINDING);
path.reset();
canvas.drawPath(path, paint);
看上面的代码,如果FillType被清除,那么这里绘制的将是一个空的内容,整个界面将是白色的,然而图片如下:
这里可以看到,并不是白色,而是蓝色,因为这里的FillType设置的是INVERSE_WINDING,也就是反转,由于它没有被清除掉,那么空路径的补集就是全集,所以整个屏幕被绘制为蓝色。
(4) rewind
rewind和reset类似,但是rewind会清除掉FillType以及所有的直线,曲线,点的数据等,但是他会保留数据结构,这样可以快速重用,提高一定的性能,例如说,重复绘制一类线段,他们的点的数量都相等,那么使用rewind可以保留装载点数据的数据结构,效率会更高。
我们来验证它是否会清除FillType
paint.setColor(Color.BLUE);
Path path = new Path();
path.addRect(100, 100, 500, 500, Path.Direction.CW);
path.addCircle(500, 500, 300, Path.Direction.CW);
path.setFillType(Path.FillType.INVERSE_WINDING);
path.rewind();
canvas.drawPath(path, paint);
效果如下
可以看出,所有数据包括FillType都被清除了。
3 PathMeasure
PathMeasure主要用来测量path,通过它,我们可以得到路径上特定的点的坐标等等。先看看他的基本方法。
(1) 构造方法
public PathMeasure()
public PathMeasure(Path path, boolean forceClosed)
如上,有两个方法,第一个就不讲解了,第二个方法中有两个参数;
path:需要测量的path
forceClosed:是否关闭path
(2) setPath
public void setPath(Path path, boolean forceClosed)
这里就是指定需要测量的path,基本和上面的第二个构造函数类似。
(3) getLength
返回当前path的总长度。
代码如下:
path.addRect(100, 100, 500, 500, Path.Direction.CW);
canvas.drawPath(path, paint);
paint.setStyle(Paint.Style.FILL);
PathMeasure pathMeasure = new PathMeasure(path, false);
float length = pathMeasure.getLength();
canvas.drawText(String.valueOf(length), 500, 500, paint);
可以看到,这里绘制的1600就是这个path轮廓的长度。
(4) getPosTan
public boolean getPosTan(float distance, float pos[], float tan[])
返回值是boolean,如过path为空,则返回false
传入参数有三个:
distance:传入距离起点的距离。
pos[]:意思是position,分别对应点的x,y坐标
tan[]:这个值比较难以理解。我们下面讲解下这个值的意义。
先来看一个动图:
代码如下:
private float[] pos;
private float[] tan;
//绘制动画路径
canvas.drawPath(animPath, paint);
if(distance < pathLength){
//获取位置和
pathMeasure.getPosTan(distance, pos, tan);
matrix.reset();
//计算方位角
float degrees = (float)(Math.atan2(tan[1], tan[0])*180.0/Math.PI);
//计算变换矩阵,用于按照方位角旋转移动的那个图像(矩阵后面有篇幅讲解,这里先不管)
matrix.postRotate(degrees, bm_offsetX, bm_offsetY);
matrix.postTranslate(pos[0]-bm_offsetX, pos[1]-bm_offsetY);
//按照矩阵(包括了旋转和位移)绘制图像
canvas.drawBitmap(bm, matrix, null);
distance += step;
}else{
distance = 0;
}
invalidate();
从上面的图中可以看到,移动的图形,不仅仅只是移动,还进行了旋转,那么这个旋转的角度如何得来,这里就靠这句代码
pathMeasure.getPosTan(distance, pos, tan);
里面的tan就是计算旋转方位角的关键。那么这个tan代表的意义何在呢,我们知道,移动图形最开始的时候的x坐标系是和cavans的平行的,在后面的移动中,这个黑色的火焰进入了一个斜线,然后他发生的旋转,此时,他的x坐标系不再平行于canvas坐标系。这里看一张图:
图中红色的线代表了黑色火焰的x轴,可以看到,他原本是平行于canvas的x轴的,后来在进入第二个斜边的时候,他沿着蓝色箭头旋转了,此时红线不再平行于canvas的x轴,红线和单位圆的交点的x,y坐标就是这里返回的tan值。通过这张图片这里的意义已经非常清晰了。
(5) getMatrix
public boolean getMatrix(float distance, Matrix matrix, int flags)
这个方法和上面的其实类似,只是他返回的是一个处理好的matrix,但是这个matrix是以左上角作为旋转点,所以需要将这个点移动到中心点。
其中还多了一个参数flags,指的是这个martrix需要什么信息。flags的值有如下两个
PathMeasure.POSITION_MATRIX_FLAG:位置信息
pathMeasure.TANGENT_MATRIX_FLAG:切边信息,方位角信息,使得图片按path旋转。
代码如下:
matrix.reset();
pathMeasure.getMatrix(distance, matrix, PathMeasure.POSITION_MATRIX_FLAG | pathMeasure.TANGENT_MATRIX_FLAG);
matrix.preTranslate(-bm_offsetX, -bm_offsetY);
canvas.drawBitmap(bm, matrix, null);
其他的相同就不贴了,这里主要是需要做一个旋转点的变换:
matrix.preTranslate(-bm_offsetX, -bm_offsetY);
(6) getSegment
public boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)
这个方法返回boolean,如果截取的长度为0则返回false,否则为true。参数意义如下
startD:起始距离
stopD:终点距离
dst:接收截取的path
startWithMoveTo:是否把截取的path,moveto到起始点。
来看一个例子:
代码如下
path.addRect(300, 300, 700, 700, Path.Direction.CW);
canvas.drawPath(path, paint);
PathMeasure pathMeasure = new PathMeasure(path, false);
Path dstPath = new Path();
pathMeasure.getSegment(0, 800, dstPath, false);
paint.setColor(Color.RED);
canvas.drawPath(dstPath, paint);
可以看到,由于这里的startWithMoveTo参数设置的是false,那么截取的path就没有moveTo到起始位置,则默认moveTo到(0,0)所以导致了绘制的红线从原点开始。设置了ture之后,图片如下:
可以看到重合了。
好了,Path和PathMeasure就到这里,上面的例子大家可以多多参考。