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:差集 REVERSE_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);
}
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX9EkaOBzaE1UeNRVT3V1MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TNyQzMyYjM0EjMyATM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
三階貝塞爾曲線: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<PointF> 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關于貝塞爾曲線的簡單應用。