1、概念
路径,可用于绘制直线,曲线构成几何路径,还可用于根据路径绘制文字
2、常用API
常用API如移动,连线,闭合,添加图形等
void moveTo(float x, float y): 移动
void lineTo(float x, float y):连线
rLineTo(float dx, float dy):表示相对位置,相对上一个点
void close():设置曲线是否闭合
void addArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle):添加弧形,startAngle开始角度,sweepAngle顺时针旋转角度
void addRect(float left, float top, float right, float bottom, Direction dir):添加矩形,Path.Direction.CW表示顺时针方向绘制,CCW表示逆时针方向
void addCircle(float x, float y, float radius, Direction dir) :添加圆
void addOval(float left, float top, float right, float bottom, Direction dir):添加椭圆
void addPath(Path src):添加一个路径
void arcTo(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean forceMoveTo):forceMoveTo,true,绘制时移动起点,false,绘制时连接最后一个点与圆弧起点
void quadTo(float x1, float y1, float x2, float y2):绘制二阶贝塞尔曲线,控制点 (x1,y1),结束点(x2,y2)
void rQuadTo(float dx1, float dy1, float dx2, float dy2):相对于起始点的相对控制点和结束点,控制点 (dx1,dy1),结束点(dx2,dy2)
void cubicTo(float x1, float y1, float x2, float y2,float x3, float y3):绘制三阶贝塞尔曲线,控制点(x1,y1) 和 (x2,y2), 结束点 (x3,y3)
void rCubicTo(float x1, float y1, float x2, float y2,float x3, float y3):绘制三阶贝塞尔曲线,相对于起始点的相对位置坐标
Path mPath = new Path();
mPath.moveTo(100, 70); //移动
mPath.lineTo(140, 800);//连线
mPath.rLineTo(40, 730);//相对上一个点的位置连线
mPath.close();//设置曲线是否闭合
mPath.addArc(200, 200, 400, 400, -225, 225);//添加弧形
//添加子图形addXXX
//添加弧形
mPath.addArc(200, 200, 400, 400, -225, 225);
//Path.Direction.CW表示顺时针方向绘制,CCW表示逆时针方向
mPath.addRect(500, 500, 900, 900, Path.Direction.CW);
//添加一个圆
mPath.addCircle(700,700, 200, Path.Direction.CW);
//添加一个椭圆
mPath.addOval(0,0,500,300, Path.Direction.CCW);
//forceMoveTo,true,绘制时移动起点,false,绘制时连接最后一个点与圆弧起点
mPath.arcTo(400, 200, 600, 400, 0, 270, false);
//添加一个路径
Path newPath = new Path();
newPath.moveTo(100, 1000);
newPath.lineTo(600, 1300);
newPath.lineTo(400, 1700);
mPath.addPath(newPath);
//添加圆角矩形, CW顺时针,CCW逆时针
RectF rectF5 = new RectF(200, 800, 700, 1200);
mPath.addRoundRect(rectF5, 20, 20, Path.Direction.CCW);
//画二阶贝塞尔曲线
mPath.moveTo(300, 500);
mPath.quadTo(500, 100, 800, 500);
//参数表示相对位置,等同于上面一行代码
mPath.rQuadTo(200, -400, 500, 0);
//画三阶贝塞尔曲线
mPath.moveTo(300, 500);
mPath.cubicTo(500, 100, 600, 1200, 800, 500);
//参数表示相对位置,等同于上面一行代码
mPath.rCubicTo(200, -400, 300, 700, 500, 0);
canvas.drawPath(mPath, mPaint);
3、PathMeasure
常用API:
void setPath(Path path, boolean forceClosed) :关联一个path
boolean isClosed():是否闭合
float getLength():获取Path的长度
boolean nextContour():跳转到下一个轮廓
boolean getSegment(float startD, float stopD, Path dst,boolean startWithMoveTo):获取片段
boolean getPosTan(float distance, float pos[], float tan[]):获取指定长度位置坐标及该点切线值
boolean getMatrix(float distance, Matrix matrix, int flags):获取指定长度位置矩阵
1、关联path,获取关联Path的长度
pathMeasure需要关联一个创建好的path,如果Path进行了调整,需要重新调用setPath方法进行关联。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, mLinePaint);
canvas.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight(), mLinePaint);
canvas.translate(getWidth() / 2, getHeight() / 2);
Path path = new Path();
path.lineTo(0,200);
path.lineTo(200,200);
path.lineTo(200,0);
/**
* pathMeasure需要关联一个创建好的path, forceClosed会影响Path的测量结果
*/
PathMeasure pathMeasure = new PathMeasure();
pathMeasure.setPath(path, true);
Log.e("TAG", "onDraw:forceClosed=true "+ pathMeasure.getLength());
PathMeasure pathMeasure2 = new PathMeasure();
pathMeasure2.setPath(path, false);
Log.e("TAG", "onDraw:forceClosed=false "+ pathMeasure2.getLength());
PathMeasure pathMeasure1 = new PathMeasure(path, false);
Log.e("TAG", "onDraw:PathMeasure(path, false) "+ pathMeasure1.getLength());
path.lineTo(200, -200);
Log.e("TAG", "onDraw:PathMeasure(path, false) "+ pathMeasure1.getLength());
//如果Path进行了调整,需要重新调用setPath方法进行关联
pathMeasure1.setPath(path, false);
Log.e("TAG", "onDraw:PathMeasure(path, false) "+ pathMeasure1.getLength());
canvas.drawPath(path, mPaint);
}
XXX: onDraw:forceClosed=true 800.0
XXX: onDraw:forceClosed=false 600.0
XXX: onDraw:PathMeasure(path, false) 600.0
XXX: onDraw:PathMeasure(path, false) 600.0
XXX: onDraw:PathMeasure(path, false) 800.0
2、获取片段
boolean getSegment (float startD, float stopD, Path dst, boolean startWithMoveTo)
这个API用于截取整个Path的片段,通过参数startD和stopD来控制截取的长度,并将截取的Path保存到dst中,最后一个参数startWithMoveTo表示起始点是否使用moveTo方法,通常为True,保证每次截取的Path片段都是正常的、完整的。
如果startWithMoveTo设置为false,通常是和dst一起使用,因为dst中保存的Path是被不断添加的,而不是每次被覆盖,设置为false,则新增的片段会从上一次Path终点开始计算,这样可以保存截取的Path片段数组连续起来
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, mLinePaint);
canvas.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight(), mLinePaint);
canvas.translate(getWidth() / 2, getHeight() / 2);
Path path = new Path();
path.addRect(-200,-200, 200,200, Path.Direction.CW);
Path dst = new Path();
dst.lineTo(-300,-300);//添加一条直线
PathMeasure pathMeasure = new PathMeasure(path, false);
//截取一部分存入dst中,并且使用moveTo保持截取得到的Path第一个点位置不变。
pathMeasure.getSegment(0, 1000, dst, true);
canvas.drawPath(path, mPaint);
}
pathMeasure.getSegment(0, 1000, dst, true)的效果
pathMeasure.getSegment(0, 1000, dst, false)的效果
3、跳转到下一个轮廓
boolean nextContour()
nextContour()方法用的比较少,比较大部分情况下都只会有一个Path而不是多个,毕竟这样会增加Path的复杂度,但是如果真有一个Path,包含了多个Path,那么通过nextContour这个方法,就可以进行切换,同时,默认的API,例如getLength,获取的也是当前的这段Path所对应的长度,而不是所有的Path的长度,同时,nextContour获取Path的顺序,与Path的添加顺序是相同的。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(getWidth() / 2, getHeight() / 2);
Path path = new Path();
path.addRect(-100,-100,100,100, Path.Direction.CW);//添加一个矩形
path.addOval(-200,-200,200,200, Path.Direction.CW);//添加一个椭圆
canvas.drawPath(path, mPaint);
PathMeasure pathMeasure = new PathMeasure(path, false);
Log.e("TAG", "onDraw:forceClosed=false "+ pathMeasure.getLength());
//跳转到下一条曲线
pathMeasure.nextContour();
Log.e("TAG", "onDraw:forceClosed=false "+ pathMeasure.getLength());
canvas.drawPath(mPath, mPaint);
}
打印结果:
2019-10-03 16:04:54.932 9902-9902/cXXX E/TAG: onDraw:forceClosed=false 800.0
2019-10-03 16:04:54.932 9902-9902/XXX E/TAG: onDraw:forceClosed=false 1256.1292
4、获取指定长度位置坐标及该点切线值
boolean getPosTan (float distance, float[] pos, float[] tan)
distance:指定的长度
pos:指定长度的位置坐标
tan:当前点在曲线上的方向与X轴之间的夹角在单位圆中的对边与邻边。tan[0]是临边长度;tan[1]是对边长度
举例:图片在圆环上不断的进行旋转
public class PathMeasurePosTan extends View {
private Paint mPaint = new Paint();
private Paint mLinePaint = new Paint(); //坐标系
private Bitmap mBitmap;
private Matrix mMatrix = new Matrix();
private float[] pos = new float[2];
private float[] tan = new float[2];
private Path mPath = new Path();
private float mFloat;
public PathMeasurePosTan(Context context) {
super(context);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(4);
mLinePaint.setStyle(Paint.Style.STROKE);
mLinePaint.setColor(Color.RED);
mLinePaint.setStrokeWidth(6);
//缩小图片
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.arrow, options);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, mLinePaint);
canvas.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight(), mLinePaint);
canvas.translate(getWidth() / 2, getHeight() / 2);
mPath.reset();
mPath.addCircle(0, 0, 200, Path.Direction.CW);
canvas.drawPath(mPath, mPaint);
mFloat += 0.01;
if (mFloat >= 1) {
mFloat = 0;
}
PathMeasure pathMeasure = new PathMeasure(mPath, false);
pathMeasure.getPosTan(pathMeasure.getLength() * mFloat, pos, tan);
//计算出当前的切线与x轴夹角的度数
double degrees = Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI;
mMatrix.reset();
//进行角度旋转
mMatrix.postRotate((float) degrees, mBitmap.getWidth() / 2, mBitmap.getHeight() / 2);
//将图片的绘制点中心与当前点重合
mMatrix.postTranslate(pos[0] - mBitmap.getWidth() / 2, pos[1] - mBitmap.getHeight() / 2);
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
invalidate();
}
}
5、Matrix 获取指定长度位置矩阵
boolean getMatrix(float distance, Matrix matrix, int flags)
distance:距离path起点的长度
matrix:将信息存放在matrix中
flags:指定存放信息的类型,POSITION_MATRIX_FLAG表示位置信息,TANGENT_MATRIX_FLAG表示当前点在曲线上的方向,对应getPosTan中的tan
例子:图片在圆环上不断的进行旋转
public class PathMeasureMatrix extends View {
private Paint mPaint = new Paint();
private Paint mLinePaint = new Paint(); //坐标系
private Bitmap mBitmap;
private Matrix mMatrix = new Matrix();
private float[] pos = new float[2];
private float[] tan = new float[2];
private Path mPath = new Path();
private float mFloat;
public PathMeasureMatrix(Context context) {
super(context);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(4);
mLinePaint.setStyle(Paint.Style.STROKE);
mLinePaint.setColor(Color.RED);
mLinePaint.setStrokeWidth(6);
//缩小图片
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.arrow, options);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, mLinePaint);
canvas.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight(), mLinePaint);
canvas.translate(getWidth() / 2, getHeight() / 2);
mPath.reset();
mPath.addCircle(0,0,200, Path.Direction.CW);
canvas.drawPath(mPath, mPaint);
mFloat += 0.01;
if (mFloat >= 1) {
mFloat = 0;
}
PathMeasure pathMeasure = new PathMeasure(mPath, false);
//将pos信息和tan信息保存在mMatrix中
pathMeasure.getMatrix(pathMeasure.getLength() * mFloat, mMatrix, PathMeasure.POSITION_MATRIX_FLAG | PathMeasure.TANGENT_MATRIX_FLAG);
//将图片的旋转坐标调整到图片中心位置
mMatrix.preTranslate(-mBitmap.getWidth() / 2, -mBitmap.getHeight() / 2);
canvas.drawBitmap(mBitmap,mMatrix, mPaint);
invalidate();
}
}
网友评论