咱们先看看Android官方文档给出的定义:javascript
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.java
这里大概翻译就是:
Path类封装了直线段,二次贝塞尔曲线和三次贝塞尔曲线的几何路径。
可使用Canvas中drawPath方法将Path画出来。Path不只可使用Paint的填充模式和描边模式,也能够用画布裁剪和或者画文字。git
总而言之,Path就是能够画出经过直线或者曲线的各类组合就能够作出不少很牛X的效果。github
至于Path能作出多牛X的效果?上图给大家看看:
canvas
要想用Path作出牛X的效果以前,就须要熟悉它的基本操做,这篇文章主要介绍的是Path的一些基本API,进阶的用法将会放在下一篇文章。api
在说这些方法以前都要作一个画笔的初始化,代码以下:数组
private void initPaint() {
mPaint = new Paint(); // 建立画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - 描边
mPaint.setStrokeWidth(10);
}复制代码
public void lineTo (float x, float y)复制代码
顾名思义,这个方法就是画一条直线的。肯定一条直线须要两个点,可是这个方法里只提供了一个点的坐标啊?那另外一个点的坐标是什么呢?这个点其实就是Path对象上次调用的最后一个点的坐标,若是在调用lineTo()方法前,并无调用过任何Path的操做,那这个点就默认为坐标原点。app
画出直线:oop
Path path = new Path(); //建立Path对象
path.lineTo(300, 300); //建立一条从原点到坐标(300,300)的直线
canvas.drawPath(path, mPaint);//画出路径复制代码
效果以下:spa
这个时候我在path.lintTo(300,300),后面再加一句 path.lineTo(100, 200); 看看效果如何?
Path path = new Path(); //建立Path对象
path.lineTo(300, 300); //建立一条从原点到坐标(300,300)的直线
path.lineTo(100, 200); //建立从(300,300)到(100,200)的一条直线
canvas.drawPath(path, mPaint);//画出路径复制代码
效果以下:
能够看到第二段线段的是从(300,300)到(100,200)的,那就能够知道lineTo方法的链接的起点是由lineTo方法上一个Path操做决定的。
public void moveTo(float x, float y)复制代码
这个方法的做用就是将下次画路径起点移动到(x,y)
仍是用上面的代码:
Path path = new Path(); //建立Path对象
path.lineTo(300, 300); //建立一条从原点到坐标(300,300)的直线
path.moveTo(0,0); //将下一次操做路径的起点坐标移到(0,0)
path.lineTo(100, 200); //建立从(0,0)到(100,200)的一条直线
canvas.drawPath(path, mPaint);//画出路径复制代码
效果以下:
能够看到在path.lineTo(100, 200);以前调用了path.moveTo(0, 0);方法,那就将lineTo的操做起始点移动到(0,0)。
public void setLastPoint(float dx, float dy)复制代码
改变上一次操做路径的结束坐标点
Path path = new Path(); //建立Path对象
path.lineTo(300, 300); //建立一条从原点到坐标(300,300)的直线
path.setLastPoint(500,500); //将上一次的操做路径的终点移动到(500,500)
path.lineTo(100, 200); //建立从(500,500)到(100,200)的一条直线
canvas.drawPath(path, mPaint);//画出路径复制代码
能够知道在执行lineTo(100, 100)时坐标点是(100,100),使用setLastPoint(500, 500)后就变成(500,500),而且也会影响上一次操做路径的终点。
在这里咱们就能够总结:
方法 | 做用 |
---|---|
moveTo | 会影响下次操做,不会影响上一次操做 |
setLastPoint | 会影响下次操做,也会影响上一次操做 |
public void close()复制代码
封闭当前路径,若是当前的点不等于路径的起始点,就会在整个操做的最后的点与起始点之间添加线段。
######怎么用:
Path path = new Path(); //建立Path对象
path.lineTo(300, 300); //建立一条从原点到坐标(300,300)的直线
path.lineTo(100, 200); //建立从(100,200)到(100,200)的一条直线
path.close(); //封闭路径
canvas.drawPath(path, mPaint);//画出路径复制代码
能够看到在执行close方法以后,在(100,200)与(0,0)之间添加了一条直线
//矩形
public void addRect(RectF rect, Direction dir)
public void addRect(float left, float top, float right, float bottom, Direction dir)
//圆形
public void addCircle(float x, float y, float radius, Direction dir)
//圆角矩形
public void addRoundRect(RectF rect, float[] radii, Direction dir)
public void addRoundRect (float left,float top,float right,float bottom,float rx,float ry,Path.Direction dir)
public void addRoundRect (RectF rect,float[] radii,Path.Direction dir)
public void addRoundRect (float left,float top,float right,float bottom,float[] radii,Path.Direction dir)
//椭圆
public void addOval(RectF oval, Direction dir)
public void addOval (float left,float top,float right,float bottom,Path.Direction dir)
//圆弧
public void addArc (RectF oval, float startAngle, float sweepAngle)
public void arcTo (RectF oval, float startAngle, float sweepAngle)
public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
// 添加Path
public void addPath (Path src)
public void addPath (Path src, float dx, float dy)
public void addPath (Path src, Matrix matrix)复制代码
public void addRect(RectF rect, Direction dir)
public void addRect(float left, float top, float right, float bottom, Direction dir)复制代码
画出一个矩形
Path path = new Path(); //建立Path对象
RectF rect = new RectF(0, 0, 400, 400);
path.addRect(rect,Path.Direction.CW);
//path.addRect(0, 0, 400, 400, Path.Direction.CW);
//这个方法与上一句是一样的效果
canvas.drawPath(path, mPaint);复制代码
addRect两个方法当中前面的全部参数其实都是肯定一个矩形,这里就不说矩形的原理了,如今重点来讲一下addRect的最后一个参数:Path.Direction dir。
这个参数是什么意思呢?这个参数就是肯定当画这个矩形的时候到底是顺时针方向画呢?仍是用逆时针方向画。
Path.Direction.CW表明顺时针,Path.Direction.CCW表明逆时针。那这个方法究竟从哪一个点开始画呢?咱们来验证一下
Path path = new Path();
path.addRect(0, 0, 400, 400, Path.Direction.CW);
path.setLastPoint(0, 300);
canvas.drawPath(path, mPaint);复制代码
咱们在addRect以后增长setLastPoint方法,从新设置最后一个点的坐标。
若是这个时候咱们将矩形的方向换成逆时针方向,看看效果如何:
Path path = new Path();
path.addRect(0, 0, 400, 400, Path.Direction.CCW);
path.setLastPoint(300,0);
canvas.drawPath(path, mPaint);复制代码
从以上两个效果就知道,addRect方向是从左上上角开始算起的。因此顺时针和逆时针的方向是会影响到绘制效果的。
public void addCircle(float x, float y, float radius, Direction dir)
画出一个圆形
Path path = new Path();
path.addCircle(200, 200, 100, Direction.CW); //建立一个圆心坐标为(200,200),半径为100的圆
canvas.drawPath(path, mPaint);复制代码
public void addRoundRect(RectF rect, float rx, float ry, Direction dir)
public void addRoundRect (float left,float top,float right,float bottom,float rx,float ry,Path.Direction dir)
public void addRoundRect (RectF rect,float[] radii,Path.Direction dir)
public void addRoundRect (float left,float top,float right,float bottom,float[] radii,Path.Direction dir)复制代码
画出一个圆角矩形
在说这个方法怎么用以前,要先说一下圆角矩形的构成原理。
请看下面这幅图:
圆角矩形的圆角其实就是一段圆弧,圆弧须要什么才能肯定它的位置和大小呢?答案就是圆心和半径,那为何上面的方法会出现两个半径呢?其实这个并非正圆的半径,而是椭圆的半径。
Path path = new Path();
RectF rect = new RectF(100,100,800,500);
path.addRoundRect(rect, 150, 100, Direction.CW); //建立一个圆角矩形
canvas.drawPath(path, mPaint);复制代码
如今咱们看一下,圆角矩形后面的那两个方法,这两个方法都有一个参数: float[] radii 。这个参数的意思就是控制圆角的四个角的半径。
这个数组至少要有8个值,若是少于8个值就会报异常。这8个值分红4组,每组的第一和第二个值分别表明圆角的x半径和y半径。
每组数据也会做用于圆角矩形的不一样位置,总结以下
值的位置 | 做用圆角矩形哪一个角 |
---|---|
0,1 | 左上角 |
2,3 | 右上角 |
4,5 | 右下角 |
6,7 | 左下角 |
//椭圆
public void addOval(RectF oval, Direction dir)
public void addOval (float left,float top,float right,float bottom,Path.Direction dir)复制代码
画一个椭圆
为了便于观察,我将椭圆中的参数的矩形用不一样颜色画出来。
Path path = new Path();
RectF rect = new RectF(100,100,800,500);
mPaint.setColor(Color.GRAY);
mPaint.setStyle(Style.FILL);
canvas.drawRect(rect, mPaint);
mPaint.setColor(Color.BLACK);
path.addOval(rect, Direction.CW);
canvas.drawPath(path, mPaint);复制代码
从效果图就能够知道,这个就是矩形的内切圆。那若是想用这个方法画出正圆应该怎么画呢?没错,就是将这个矩形变成正方形,画出来的圆就是正圆了。如今验证一下:
Path path = new Path();
RectF rect = new RectF(100,100,800,800); //将矩形变成正方形
mPaint.setColor(Color.GRAY);
mPaint.setStyle(Style.FILL);
canvas.drawRect(rect, mPaint);
mPaint.setColor(Color.BLACK);
path.addOval(rect, Direction.CW);
canvas.drawPath(path, mPaint);复制代码
//圆弧
public void addArc (RectF oval, float startAngle, float sweepAngle)
public void arcTo (RectF oval, float startAngle, float sweepAngle)
public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)复制代码
先说一下 startAngle 和 sweepAngle 这两个参数的意思。
参数 | 意思 |
---|---|
startAngle | 开始的角度 |
sweepAngle | 扫过的角度 |
startAngle 是表明开始的角度,那么Android中矩形的0°是从哪里开始呢?其实矩形的0°是在矩形的右边的中点,按顺时针方向逐渐增大。
如图:
sweepAngle 扫过的角度就是从起点角度开始扫过的角度,并非指终点的角度。例如若是你的startAngle是90°,sweepAngle是180°。那么这个圆弧的终点应该在270°,而不是在180°。
如今验证一下看看:
Path path = new Path();
RectF rect = new RectF(300,300,1000,800);
mPaint.setColor(Color.GRAY);
mPaint.setStyle(Style.FILL);
canvas.drawRect(rect, mPaint);
mPaint.setColor(Color.BLACK);
path.addArc(rect, 90, 180);
canvas.drawPath(path, mPaint);复制代码
效果以下:
知道了addArc的用法以后,咱们来看一下arcTo这个方法,这个方法也是用来画圆弧的,可是与addArc有些不一样,总结以下:
方法 | 做用 |
---|---|
addArc | 直接添加一段圆弧 |
arcTo | 添加一段圆弧,若是圆弧的起点与上一次Path操做的终点不同的话,就会在这两个点连成一条直线 |
举个例子:
Path path = new Path();
RectF rect = new RectF(300,300,1000,800);
mPaint.setColor(Color.GRAY);
mPaint.setStyle(Style.FILL);
canvas.drawRect(rect, mPaint);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Style.STROKE);
path.lineTo(100, 100); //用path画一条从(0,0)到(100,100)的直线
path.arcTo(rect, 90, 180); //用arcTo方法画一段圆弧
canvas.drawPath(path, mPaint); //直线终点(100,100)与圆弧起点会连成一条直线复制代码
效果以下:
若是你不想这两个点连线的话,arcTo在一个方法中有forceMoveTo的参数,这个参数若是设为true就说明将上一次操做的点设为圆弧的起点,也就是说不会将圆弧的起点与上一次操做的点链接起来。若是设为false就会链接。
来验证一下:
Path path = new Path();
RectF rect = new RectF(300,300,1000,800);
mPaint.setColor(Color.GRAY);
mPaint.setStyle(Style.FILL);
canvas.drawRect(rect, mPaint);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Style.STROKE);
path.lineTo(100, 100); //用path画一条从(0,0)到(100,100)的直线
path.arcTo(rect, 90, 180,true); //用arcTo方法画一段圆弧
canvas.drawPath(path, mPaint); //直线终点(100,100)与圆弧起点不会连成一条直线复制代码
效果以下:
//添加Path:
public void addPath (Path src)
public void addPath (Path src, float dx, float dy)
public void addPath (Path src, Matrix matrix)复制代码
######有什么用:
将两个Path合并在一块儿
######怎么用:
这里先讲addPath的前两个方法,最后那个方法等写到Matrix才细讲。
Path path = new Path();
Path src = new Path();
path.addRect(0, 0, 400, 400, Path.Direction.CW); //宽高为400的矩形
src.addCircle(200, 200, 100, Path.Direction.CW); //圆心为(200,200)半径为100的正圆
path.addPath(src);
canvas.drawPath(path, mPaint);复制代码
######效果以下:
addPath的第二个方法的 dx 和 dy 两个参数是什么意思呢?
其实它们是表明添加path后的位移值。
例如,上面这个例子,若是我将path.addPath(src);改为path.addPath(src,200,0);会出现什么现象呢?这时候src画的圆的圆心的坐标会移动到(400,200)。
让咱们来验证一下:
Path path = new Path();
Path src = new Path();
path.addRect(0, 0, 400, 400, Path.Direction.CW); //宽高为400的矩形
src.addCircle(200, 200, 100, Path.Direction.CW); //圆心为(200,200)半径为100的正圆
//path.addPath(src);
path.addPath(src,200,0);
canvas.drawPath(path, mPaint);复制代码
效果以下:
path画出宽高为400的矩形,src画出一个圆心为(0,0),半径为100的圆。path.addPath将src合并到一块儿,并将src的中心设置为(200,200)。
public void set(Path src)复制代码
将新的path赋值到现有的path
Path path = new Path();
Path src = new Path();
path.addRect(0, 0, 400, 400, Path.Direction.CW);
src.addCircle(200, 200, 100, Path.Direction.CW);
path.set(src); // 至关于 path = src;
canvas.drawPath(path, mPaint);复制代码
效果以下:
这个方法就是将path以前的矩形变成圆形。
public void offset (float dx, float dy)
public void offset (float dx, float dy, Path dst)复制代码
将path进行平移
Path path = new Path();
path.addRect(0, 0, 400, 400, Path.Direction.CW);
canvas.drawPath(path, mPaint);
mPaint.setColor(Color.RED); //将画笔变成红色
path.offset(100,0); //将path向右平移
canvas.drawPath(path, mPaint);复制代码
offset的第二个方法的第三个参数的意思就是将平移后的path存储到dst参数中。
若是传入dst不为空,将平移后的状态存储到dst中,不影响当前path。dst为空,平移做用当前的path,至关于第一个方法。
如今验证一下:
Path path = new Path();
Path dst = new Path();
path.addRect(0, 0, 400, 400, Direction.CW); //path添加矩形
dst.addCircle(100,100, 100, Direction.CW); //dst添加圆形
path.offset(100,0,dst); //将平移后的path存储到dst
canvas.drawPath(dst, mPaint);复制代码
效果以下:
######方法预览:
public void reset()复制代码
这个方法很简单,就是将path的全部操做都清空掉。
public boolean isConvex ()复制代码
判断path是否为凸多边形,若是是就为true,反之为false。
要理解这个方法首先,咱们要知道什么是凸多边形。
凸多边形的概念:
1.每一个内角小于180度
2.任何两个顶点间的线段位于多边形的内部或边界上。
也就是说矩形,三角形,直线都是凸多边形,可是五角星那种形状就不是。如今咱们用代码验证一下:
代码以下:
Path path = new Path();
path.moveTo(600,600);
path.lineTo(500,700);
path.lineTo(380,700);
path.lineTo(500,780);
path.close();
Log.e("Path", "===============path.isConvex() " + path.isConvex());
canvas.drawPath(path,mPaint);复制代码
效果以下:
打印的结果为:
E Path : ===============path.isConvex() false复制代码
由于该图形并非凸多边形,因此返回false。
但这里有个坑,若是我直接使用addRect方法,而后用setLastPoint来将这个矩形变成凹多边形。
代码以下:
Path path = new Path();
RectF rect = new RectF(0,0,400,400);
path.addRect(rect, Direction.CCW);
path.setLastPoint(100, 300);
Log.e("Path", "===============path.isConvex() " + path.isConvex());
canvas.drawPath(path,mPaint);复制代码
效果以下:
能够看出图形是一个凹多边形,可是打印的信息倒是:
E Path : ===============path.isConvex() true复制代码
这个地方我一直也想不明白,为何会返回true。等我之后有思路了再来解决这个问题吧。
######方法预览:
public boolean isEmpty ()复制代码
######有什么用:
判断path中是否包含内容:
Path path = new Path();
Log.e("path.isEmpty()","==============path.isEmpty()1: " + path.isEmpty());
path.lineTo(100,100);
Log.e("path.isEmpty()","==============path.isEmpty()2: " + path.isEmpty());复制代码
Log输出的结果:
E path.isEmpty(): ==============path.isEmpty()1: true
E path.isEmpty(): ==============path.isEmpty()2: false复制代码
###4.3 isRect:
######方法预览:
public boolean isRect (RectF rect)复制代码
判断path是不是一个矩形,若是是一个矩形的话,将矩形的信息存到参数rect中。
path.lineTo(0,400);
path.lineTo(400,400);
path.lineTo(400,0);
path.lineTo(0,0);
RectF rect = new RectF();
boolean b = path.isRect(rect);
Log.e("Rect","isRect:"+b+"| left:"+rect.left+"| top:"+rect.top+"| right:"+rect.right+"| bottom:"+rect.bottom);复制代码
Log输出的结果:
E Rect : ======isRect:true| left:0.0| top:0.0| right:400.0| bottom:400.0复制代码
####参考资料: