1.前言
Path是Android中用来封装几何路径的一个类,Path主要用于绘制复杂的图形轮廓,比如折线,圆弧以及各种复杂图案。
2.实现分析
Android为我们提供的Path类是一个非常有用的类,它可以预先在View上将N个点按照顺序,将这N个点依次连接成一条“路径”,然后使用Canvas的drawPath(Path path, Paint paint)方法即可沿着路径进行绘制,事实上,Android不仅仅为我们提供了路径绘制的方法还提供了PathEffect来添加绘制效果。
(1)Path类
lineTo(float x, float y):以上一个点为起点到自定义点为终点画直线。参数一和参数二分别表示自定义点的横坐标和纵坐标(若没有设置起始点时,默认的起始点是(0, 0))
moveTo(float x, float y):移动起始点的坐标至(x, y)。参数一和参数二分别表示将起始点移动到的坐标
quadTo(float x1, float y1, float x2, float y2):靠近控制点,从起点到终点画一条二次贝塞尔曲线(起点默认为Canvas的左上角)。参数一和参数二表示控制点的坐标,参数三和参数四表示终点的坐标
cubicTo(float x1, float y1, float x2, float y2, float x3, float y3):靠近控制点,从起点到终点画一条三次贝塞尔曲线(起点默认为Canvas的左上角)。参数一和参数二表示第一个控制点的坐标,参数三和参数四表示第二个控制点的坐标,参数五和参数六表示终点的坐标
rLineTo(float dx, float dy):以当前的点做为基点,以直线的形式连接到 (currentX + dx , currentY + dy)。参数一和参数二分别表示沿x轴和y轴移动的距离
rMoveTo(float dx, float dy):移动前一个点。参数一和参数二分别表示将前一个点沿x轴移动的距离和沿y轴移动的距离
rQuadTo(float dx1, float dy1, float dx2, float dy2):靠近控制点,从起点到终点画一条二次贝塞尔曲线(若有上一个点起点为上一个点,若没有上一个点起点默认为Canvas的左上角)。
addArc(RectF oval, float startAngle, float sweepAngle):添加一个指定的弧路径。参数一表示RectF的对象表示指定的矩形轮廓,参数二表示起始角度,参数三表示顺时针画弧的角度
addPath(Path src) :将传入的路径添加到当前路径。参数一表示添加到当前路径的路径
addCircle(float x, float y, float radius, Path.Direction dir):添加一个指定的圆路径。参数一和参数二分别表示圆心的横坐标和纵坐标,参数三表示圆的半径,参数四表示圆的风格
addOval(RectF oval, Path.Direction dir):添加一个指定的椭圆路径。参数一表示RectF的对象表示指定的矩形轮廓,参数二表示椭圆的风格
addRect(float left, float top, float right, float bottom, Path.Direction dir) :添加一个指定的矩形路径。参数一和参数二表示矩形左上角的坐标,参数三和参数四表示矩形右下角的坐标,参数五表示矩形的风格
这些方法和addArc有很明显的区别,就是多了一个Path.Direction参数,其他呢都大同小异,除此之外不知道大家还发现没有,addArc是往Path中添加一段弧,说白了就是一条开放的曲线,而上述几种方法都是一个具体的图形,或者说是一条闭合的曲线,Path.Direction的意思就是标识这些闭合曲线的闭合方向。Path.Direction只有两个常量值CCW和CW分别表示逆时针方向闭合和顺时针方向闭合
arcTo(RectF oval, float startAngle, float sweepAngle) :添加指定的新的弧线做为新的路径。参数一表示RectF的对象表示指定的矩形轮廓,参数二表示起始角度,参数三表示顺时针画弧的角度
arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo):添加指定的新的弧线做为新的路径。参数一表示RectF的对象表示指定的矩形轮廓,参数二表示起始角度,参数三表示顺时针画弧的角度,参数四表示是否要连接弧线的起始点
getFillType():得到绘制路径的填充模式
setFillType():设置 绘制路径的填充模式
isInverseFillType():得到填充模式是否为逆填充模式
reset():清除掉path里的线条和曲线,但是不会改变它的fill-type(后面setFillType再说)
rewind():清除掉path里的线条和曲线,但是会保留内部的数据结构以便重用
set(Path src):用src的内容替换原path的内容。参数一表示需要替换的路径
close():关闭当前路径(构建一条末点和起始点的线段)
下面我们对与上面的几个方法分别举例,这样可以更加直观的理解这几个方法的含义
首先使用lineTo(float x, float y)方法画条黑色的线段
主要的代码和效果图如下
package com.example.administrator.pathview;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by ChuPeng on 2016/11/1.
*/
public class PathView extends View
{
private Paint paint;
private Path path;
public PathView(Context context)
{
super(context);
}
public PathView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public PathView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public PathView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{
super(context, attrs, defStyleAttr, defStyleRes);
}
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint = new Paint();
//设置paint的属性
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
//实例化路径
path = new Path();
//连接路径到指定的点
path.lineTo(200, 150);
//绘制路径
canvas.drawPath(path, paint);
}
}
从效果图中可以看出从起始点到自定义默认点(500, 500)连线,默认的其实点为(0, 0)点
接下来使用moveTo(float x, float y)的方法来移动起始点
主要的代码和效果图如下
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint = new Paint();
//设置paint的属性
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
//实例化路径
path = new Path();
//移动起始点至(100, 100)
path.moveTo(100, 100);
//连接路径到指定的点
path.lineTo(200, 200);
path.lineTo(200, 100);
//绘制路径
canvas.drawPath(path, paint);
}
此时的效果图如下
从效果图中可以看出此时的起始点更上面的起始点相比较,沿x轴和y轴分别移动了100px,并且又新画了一条线段
接下来使用close()的方法构建一条末点到起始点的线段
主要的代码和效果图如下
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint = new Paint();
//设置paint的属性
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
//实例化路径
path = new Path();
//移动起始点至(100, 100)
path.moveTo(100, 100);
//连接路径到指定的点
path.lineTo(200, 200);
path.lineTo(200, 100);
//构建一条末点和起始点的线段
path.close();
oval = new RectF(300, 300, 400, 400);
//绘制路径
canvas.drawPath(path, paint);
}
从效果图中可以看出close()方法将路径的起始点和终止点连接了起来,构成了一个封闭的图形
接下来使用arcTo(RectF oval, float startAngle, float sweepAngle) 来画一条弧线
主要的代码和效果图如下
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint = new Paint();
//设置paint的属性
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
//实例化路径
path = new Path();
//移动起始点至(100, 100)
path.moveTo(100, 100);
oval = new RectF(100, 100, 200, 200);
path.arcTo(oval, 0, 90);
//绘制路径
canvas.drawPath(path, paint);
}
此时的效果图如下
从效果图中可以看出,画弧线首先需要构建出一个矩形区域,然后在这个矩形区域内画内切于矩形的圆或者椭圆,然后根据参数中的起始角度和终止角度来截取圆弧
在这里由于使用Path生成的路径都是连贯的,虽然我们使用arcTO绘制的仅仅是一段弧线,但是这段弧线最终都会与起始点连接,如果不想要这条与起始点连接的线段可以使用另一个重载方法arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
在Path类中除了上面介绍的几个XXXTo方法外还有一套相对应的rXXXTo方法,这些rXXXTo方法其实跟上面的那些XXXTo方法在功能上都是一样的,唯一的区别就是rXXXTo方法的参考坐标系是相对的,而XXXTo方法的参考坐标系始终参考画布的原点坐标。其实,这个前缀“r”也就是relative(相对)的简写!
在Path类中提供的XXXTo方法用来连接Path中的曲线,在上面都已经做了简单的介绍。Path类中还为我们提供了一系列的addXXX方法,这一系列的方法可以让我们直接往Path中添加一些曲线
我们先使用addArc(RectF oval, float startAngle, float sweepAngle)添加一个指定的弧线
主要的代码和效果图如下
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint = new Paint();
//设置paint的属性
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
//实例化路径
path = new Path();
//移动起始点至(100, 100)
path.moveTo(100, 100);
//连接路径到指定的点
path.lineTo(200, 200);
path.lineTo(200, 100);
//构建一条末点和起始点的线段
path.close();
oval = new RectF(300, 300, 400, 400);
//添加一条路径
path.addArc(oval, 0, 90);
//绘制路径
canvas.drawPath(path, paint);
}
此时的效果图如下
通过效果图可以看出,在我们往Path中添加了一条弧线后,该弧线并没有与该线段所连接
Path类中还可以设置填充模式,在这里总共定义了四种填充模式分别是WINDING、EVEN_ODD、INVERSE_WINDING和INVERSE_EVEN_ODD,在这里可以用一张图表示这四种模式的差别
Path类中还为我们提供了一些方法模拟低阶的贝塞尔曲线,你肯定会有疑问什么叫做贝塞尔曲线?其实贝塞尔就是使用三个或多个点来确定的一条曲线,你只需要一个起点,一个终点和至少0个控制点就可以定义一条贝塞尔曲线(PS:以下的图片和公式均来自互联网)
当控制点为0个时只有起点和终点,此时的贝塞尔曲线就相当于一条直线,这时的贝塞尔曲线为一阶贝塞尔曲线
一阶贝塞尔曲线
一阶贝塞尔曲线没有控制点,只有起始点和终止点,此时就相当于一条直线
一阶贝塞尔曲线的公式为:
二阶贝塞尔曲线
二阶贝塞尔曲线和一阶贝塞尔相比较最显著的特点就是多了一个控制点,就是因为有了这个控制点贝塞尔曲线才从有了弧度,在上面的图中P0为初始点,P2为终止点,P1为控制点。Path类中提供的quadTo方法可以构建二阶贝塞尔曲线
二阶贝塞尔曲线的公式为:
三阶贝塞尔曲线
三阶贝塞尔曲线与二阶贝塞尔曲线相比较又多了一个控制点,三阶贝塞尔曲线有两个控制点,在上面的图中P0为初始点,P3为终止点,P1和P2为控制点。Path类中提供的cubicTo方法可以构建三阶贝塞尔曲线
高阶贝塞尔曲线在Path类中并没有提供相应的方法,对于我们来说目前Path类中提供的方法足够使用了,有兴趣的小伙伴可以去维基百科中了解一下,这里就不在做过多的介绍了。
3.总结到目前为止Paint、Canvas和Path的基本使用方法都已经介绍完毕了,只要能够灵活的运用这些方法就可以满足我们大多数的需要
以上Demo的源代码地址:点击打开链接