Path作为UI绘制的重要的一个类,在官方文档上对于的介绍如下:
Path封装了由直线段,二次曲线和三次曲线组成的复合几何路径,它可以用canvas.drawPath()进行绘制,填充,描画,或者可以用于剪切或者绘制路径上的文字。
下面关于它常见的API进行一个简单的记录:
方法 | 讲解 |
lineTo(float x, float y) | 绘制直线,连接上一点和当前点 |
moveTo(float x, float y) | 移动画笔,将画笔的开始位置移动到(x,y) |
arcTo(RectF oval, float startAngle, float sweepAngle) | 绘制圆弧,oval绘制圆弧的矩形局域,startAngle其实角度,sweepAngle旋转角度 |
quadTo(float x1, float y1, float x2, float y2) | 绘制二阶贝塞尔曲线,当前path位置为起点,(x1,y1)为控制点(x2,y2)终点 |
cubicTo(float x1, float y1, float x2, float y2,float x3, float y3) | 绘制三阶贝塞尔曲线 当前path位置为起点,(x1,y1)为控制点1,(x2,y2)控制点2,(x3,y3)终点 |
add方法
方法 | 讲解 |
addArc(RectF oval, float startAngle, float sweepAngle) | 添加圆弧到当前path,oval添加区域,startAngle开始角度,sweepAngle圆弧旋转的角度 |
addCircle(float x, float y, float radius, Direction dir) | 添加圆到当前path,(x,y)圆心的坐标,radius半径,dir线的闭合方向(CW顺时针方向 ,CCW逆时针方向) |
addOval(RectF oval, Direction dir) | 添加椭圆到当前path,oval绘制区域,dir:闭合方向 |
addRect(RectF rect, Direction dir) | 添加矩形到当前path |
addRoundRect(RectF rect, float rx, float ry, Direction dir) | 添加圆角矩形,rect区域,rx横轴半径,ry纵轴半径,dir闭合方向,该方法的圆角是统一大小的 |
addRoundRect(RectF rect, float[] radii, Direction dir) | 添加圆角矩形(非统一),rect矩形区域,radii四个圆角矩形分别对应的横轴半径和纵轴半径,一个8个,dir闭合反向 |
addPath(Path src) | 添加Path,添加path到当前path |
addPath(Path src, float dx, float dy) | 添加平移后的path到当前path,src需要平移的path,(dx,dy)平移后的x,y坐标 |
addPath(Path src, Matrix matrix) | 添加经过矩阵变幻后的path |
Fill type -Path的填充模式
方法 | 讲解 |
setFillType(FillType ft) | 设置填充模式,ft取值共有: EVEN_ODD:取Path所在并不相交区域 INVERSE_EVEN_ODD :取Path未占或相交区域 WINDING:取Path所有区域,默认模式 INVERSE_WINDING:取Path所有未占区域 |
getFillType() | 获取当前path的填充模式 |
isInverseFillType() | 判断当前填充模式是否是反向规则,即(INVERSE_XX) |
toggleInverseFillType() | 当前填充模式与其反向规则模式进行反向取 |
其他零散重要的方法
方法 | 讲解 |
close() | 封闭当前path,连接起点和终点 |
reset() | 清空path,相当于new Path |
rewind() | 清空path上面的直线和曲线,保留数据结构 |
set(Path src) | 用名为src的path替换当前path |
op(Path path, Op op) | 当前path与名为path的路径进行布尔运算: DIFFERENCE:差集 INTERSECT:交集, UNION:并集 XOR:异或 |
offset(float dx, float dy) | 平移path,dx-x轴上的移动距离,dy-Y轴上的移动距离 |
offset(float dx, float dy, Path dst) | 平移dstPath |
transform(Matrix matrix) | 对当前path进行矩阵变幻 |
setLastPoint(float dx, float dy) | 设置当前path的终点 |
isEmpty() | 判断当前path是否是空 |
isConvex() | 判断当前path是否为凸多边形 |
isRect(RectF rect) | 如果当前path是矩形,那么储存放入rect中 |
以上的是Path的创建方法,在日常的开发工作中我们可以根据自己需要调用合适API,在上篇文章androidUI之贝塞尔曲线中我们主要简介了贝塞尔曲线的原理以及算法,在这里我们主要以Path绘制贝塞尔曲线为例子进行API的简单实用。
Path绘制二阶贝塞尔曲线:quadTo()
private void init() {
mPath = new Path();
xC1 = 300;
yC1 = 60;
xEnd = 500;
yEnd = 300;
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.RED);
pPaint = new Paint();
pPaint.setStyle(Paint.Style.FILL);
pPaint.setColor(Color.BLACK);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.moveTo(100, 100);
mPath.quadTo(xC1, yC1, xEnd, yEnd);//二阶贝塞尔曲线
canvas.drawPath(mPath, mPaint);
canvas.drawCircle(100, 100, 10, pPaint);
canvas.drawCircle(xEnd, yEnd, 10, pPaint);
//控制点
pPaint.setColor(Color.GREEN);
canvas.drawCircle(xC1, yC1, 10, pPaint);
}
三阶贝塞尔曲线:cublicTo
private void init() {
mPath = new Path();
xStart = 100;
yStart = 100;
xC1 = 300;
yC1 = 60;
xC2 = 500;
yC2 = 500;
xEnd = 800;
yEnd = 300;
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.RED);
pPaint = new Paint();
pPaint.setStyle(Paint.Style.FILL);
pPaint.setColor(Color.BLACK);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.moveTo(xStart, yStart);
mPath.cubicTo(xC1, yC1, xC2, yC2, xEnd, yEnd);//三阶贝塞尔曲线
canvas.drawPath(mPath, mPaint);
//控制点
canvas.drawCircle(xC1, yC1, 10, pPaint);
canvas.drawCircle(xC2, yC2, 10, pPaint);
//起始点
pPaint.setColor(Color.GREEN);
canvas.drawCircle(xStart, yStart, 10, pPaint);
canvas.drawCircle(xEnd, yEnd, 10, pPaint);
}
多阶:利用Path+德卡斯特利奥算法:
package com.barry.lsn_7_path_bezier.hy;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* @author huanghaha
* @desc 多阶贝塞尔曲线
*/
public class OtherBesselView extends View {
private Path mPath;
private Paint mPaint;//点线之间的画笔
private Paint pPaint;//贝塞尔曲线的画笔
private float xStart, yStart;
private float xEnd, yEnd;
private List cPoints = new ArrayList<>();
public OtherBesselView(Context context) {
super(context);
init();
}
public OtherBesselView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public OtherBesselView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public OtherBesselView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void init() {
mPath = new Path();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.RED);
pPaint = new Paint();
pPaint.setStyle(Paint.Style.STROKE);
pPaint.setStrokeWidth(5);
pPaint.setColor(Color.GREEN);
Random random = new Random();
for (int i = 0; i < 6; i++) {
int cX = random.nextInt(600) + 100;
int cY = random.nextInt(600) + 100;
PointF p = new PointF(cX, cY);
cPoints.add(p);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//1.画出控制点以及他们之间的线段-仅仅为了对照
for (int i = 0; i < cPoints.size(); i++) {
PointF pointF = cPoints.get(i);
if (i > 0) {
canvas.drawLine(cPoints.get(i - 1).x, cPoints.get(i - 1).y, pointF.x, pointF.y, mPaint);
}
canvas.drawCircle(pointF.x, pointF.y, 10, mPaint);
}
//2.根据德卡斯特利奥算法画出贝塞尔曲线
buildBezierPoints(canvas);
}
/**
* 绘制贝塞尔曲线
*/
private void buildBezierPoints(Canvas canvas) {
int order = cPoints.size() - 1; //当前贝塞尔曲线的阶数
float delta = 1.0f / 100;// 绘制的密度,(如果密度太小可能不能绘制完所有的曲线)
for (float t = 0.0f; t <= 1.0f; t += delta) {
PointF pointF = new PointF(deCusterLeoX(order, 0, t), deCusterLeoY(order, 0, t));//贝塞尔曲线上的点
//使用path连接点
if (t == 0.0f) {//移动画笔到第一个点
mPath.moveTo(pointF.x, pointF.y);
} else {//连接点
mPath.lineTo(pointF.x, pointF.y);
}
}
canvas.drawPath(mPath, pPaint);
}
/**
* 利用递归思想求出在t-比例不断变动的情况先贝塞尔曲线上的点
* 由于递归的计算有压栈的特点,所以这儿的j进入的时候传入的0,为方便理解可以打开注释
*
* @param order 阶数
* @param j 当前控制点
* @param t 比例
* @return
*/
private float deCusterLeoX(int order, int j, float t) {
// Log.e("msg", "开始计算的条件" + " order=" + order + " j=" + j + " t=" + t);
float reuslt = 0.0f;
if (order == 1) {//一阶段
reuslt = (1 - t) * cPoints.get(j).x + t * cPoints.get(j + 1).x;
} else {
float val1 = (1 - t) * deCusterLeoX(order - 1, j, t);
float val2 = t * deCusterLeoX(order - 1, j + 1, t);
reuslt = val1 + val2;
}
// Log.e("msg", "开始计算的结果" + reuslt);
return reuslt;
}
private float deCusterLeoY(int order, int j, float t) {
if (order == 1) {//一阶段
return (1 - t) * cPoints.get(j).y + t * cPoints.get(j + 1).y;
} else {
return (1 - t) * deCusterLeoY(order - 1, j, t) + t * deCusterLeoY(order - 1, j + 1, t);
}
}
}
以上便是path关于贝塞尔曲线的简单应用。