Android原生绘图(三):Path

1,680 阅读6分钟

知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

1.简介

前面利用Paint,Canvas已经可以绘制出各式各样的简单图形了,本篇讲解Path意为路径,canvas中有drawPath函数按照路径绘制图形。利用Path可以更方便的添加节点,控制形状,对于复杂的形状简单的通过Canvas的绘制函数进行绘制繁琐且麻烦,使用Path可能会简化操作,一般复杂的图形都是用Path进行绘制。

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.

1.1 构造函数

Path()
创建一个空的Path

Path(Path src)
利用已有Path生成新Path。

2.直线路径

Path用于绘制路径,所以如果是绘制点用不到Path,最简单的路径就是由直线组成,Path提供了简单的函数实现直线的绘制。

  • moveTo
  • lineTo
  • close
  • setLastPoint
  • rMoveTo
  • rLineTo

moveTo
public void moveTo(float x, float y) ;
moveTo (float x, float y):直线的开始点(也可能是下一个形状的开始点)即将直线路径的绘制位置定在(x,y)的位置;


rMoveTo

在前一个点的基础上开始绘制新的直线。

lineTo
public void lineTo(float x, float y) ;
void lineTo (float x2, float y2):结束点或者下一次绘制直线路径的开始点;


rLineto:在前面路径的基础上连续绘制,如果前面一个点是(x,y),rLineTo(x1,y1)相当于lineTo(x+x1,y+y1),如果前面没有调用moveTo,相当于从(0,0)开始绘制。


多次调用lineTo可以一直调用。如果一个直线开始绘制没有调用moveTo,默认第一个点从(0,0)点开始绘制。lineTo添加的是直线而不仅仅是点。

public void close() ;
void close ():如果绘制的直线没有形成闭环,调用Close()会将路径首尾点连接起来,形成闭环;

/**
 * Sets the last point of the path.
 *
 * @param dx The new X coordinate for the last point
 * @param dy The new Y coordinate for the last point
 */
public void setLastPoint(float dx, float dy) {
    isSimplePath = false;
    nSetLastPoint(mNativePath, dx, dy);
}

未调用close函数

Path mPath = new Path();

mPath.moveTo(100,70);
mPath.lineTo(140,180);
mPath.lineTo(250,330);
mPath.lineTo(400,630);
mPath.lineTo(100,830);
canvas.drawPath(mPath, mPaint2);

调用close函数

3.圆弧路径 addArc

public void addArc(RectF oval, float startAngle, float sweepAngle) {
    addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle);
}

//利用坐标点代替RectF
public void addArc(float left, float top, float right, float bottom, float startAngle,
        float sweepAngle) {
    isSimplePath = false;
    nAddArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle);
}

添加圆弧路径:
oval:矩形区域
startAngle:开始弧度
sweepAngle:弧形扫过的弧度
x轴正方向为0度,然后顺时针绘制。

RectF rectF = new RectF(100, 100, 500, 500);
RectF rectF1 = new RectF(600, 600, 1000, 1000);

Path path = new Path();
path.addArc(rectF, 180, 180);
path.addArc(rectF1, 0, 270);
canvas.drawPath(path, mPaint2);

4.添加圆弧路径 arcTo

和addArc类似,添加一个圆弧到path,如果圆弧的起点和上次最后一个坐标点不相同,就连接两个点。

public void arcTo(RectF oval, float startAngle, float sweepAngle,
                  boolean forceMoveTo) {
    arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, forceMoveTo);
}

public void arcTo(RectF oval, float startAngle, float sweepAngle) {
    arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, false);
}

public void arcTo(float left, float top, float right, float bottom, float startAngle,
        float sweepAngle, boolean forceMoveTo) {
    isSimplePath = false;
    nArcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
}

参数:forceMoveTo
true 将最后一个点移动到圆弧起点,即不连接最后一个点与圆弧起点,
false 不移动,而是连接最后一个点与圆弧起点

RectF rectF = new RectF(100, 100, 500, 500);
Path path = new Path();
path.moveTo(0, 0);
path.lineTo(100, 100);
path.arcTo(rectF, 0, 270, true);
canvas.drawPath(path, mPaint2);

path.reset();
RectF rectF1 = new RectF(600, 600, 1000, 1000);
path.setLastPoint(0, 500);
path.arcTo(rectF1, 0, 270, false);
canvas.drawPath(path, mPaint2);

5.添加圆形路径 addCircle

public void addCircle(float x, float y, float radius, Direction dir) {
    isSimplePath = false;
    nAddCircle(mNativePath, x, y, radius, dir.nativeInt);
}

x,y:圆心坐标
Radius:半径
Direction:方向
Path.Direction:设置添加path的顺序,CW顺时针,CCW逆时针,在添加图形时确定闭合顺序

public enum Direction {
    /** clockwise */
    CW  (0),    // must match enum in SkPath.h
    /** counter-clockwise */
    CCW (1);    // must match enum in SkPath.h
}
Path path = new Path();
path.addCircle(400, 400, 300, Path.Direction.CW);
canvas.drawPath(path, mPaint2);

6.添加椭圆路径 addOval

public void addOval(RectF oval, Direction dir) {
    addOval(oval.left, oval.top, oval.right, oval.bottom, dir);
}

public void addOval(float left, float top, float right, float bottom, Direction dir) {
    isSimplePath = false;
    nAddOval(mNativePath, left, top, right, bottom, dir.nativeInt);
}
Path path = new Path();
path.addOval(100,100, 400, 600, Path.Direction.CW);
canvas.drawPath(path, mPaint2);

7.添加Path addPath

//合并path之前做偏移处理
public void addPath(Path src, float dx, float dy) {
    isSimplePath = false;
    nAddPath(mNativePath, src.mNativePath, dx, dy);
}

//合并两个Path
public void addPath(Path src) {
    isSimplePath = false;
    nAddPath(mNativePath, src.mNativePath);
}

//添加路径之前做matrix变换
public void addPath(Path src, Matrix matrix) {
    if (!src.isSimplePath) isSimplePath = false;
    nAddPath(mNativePath, src.mNativePath, matrix.native_instance);
}
  • addPath(Path src, float dx, float dy)进行了位移之后再添加进当前path中。
  • addPath(Path)将两个Path合并成为一个。
  • addPath(Path src, Matrix matrix)添加到当前path之前先使用Matrix进行变换。

1.直接合并

mPath.moveTo(100,70);
mPath.lineTo(140,180);
mPath.lineTo(250,330);
mPath.lineTo(400,630);
mPath.lineTo(100,830);

Path newPath = new Path();
newPath .moveTo(100,1000);
newPath .lineTo(600,1300);
newPath .lineTo(400,1700);

mPath.addPath(newPath);


2.平移后合并

mPath.moveTo(100,70);
mPath.lineTo(140,180);
mPath.lineTo(250,330);
mPath.lineTo(400,630);
mPath.lineTo(100,830);

Path newPath = new Path();
newPath.moveTo(100,1000);
newPath.lineTo(600,1300);
newPath.lineTo(400,1700);

mPath.addPath(newPath,300,100);

3.Matrix变换后合并

mPath.moveTo(100,70);
mPath.lineTo(140,180);
mPath.lineTo(250,330);
mPath.lineTo(400,630);
mPath.lineTo(100,830);

Path newPath = new Path();
newPath.moveTo(100,1000);
newPath.lineTo(600,1300);
newPath.lineTo(400,1700);

Matrix matrix = new Matrix();
matrix.postScale(0.5f,0.5f);
mPath.addPath(newPath,matrix);

8.添加矩形

public void addRect(RectF rect, Direction dir) {
    addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
}

public void addRect(float left, float top, float right, float bottom, Direction dir) {
    detectSimplePath(left, top, right, bottom, dir);
    nAddRect(mNativePath, left, top, right, bottom, dir.nativeInt);
}
public void addRoundRect(RectF rect, float rx, float ry, Direction dir) {
    addRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, dir);
}

public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry,
        Direction dir) {
    isSimplePath = false;
    nAddRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt);
}

public void addRoundRect(RectF rect, float[] radii, Direction dir) {
    if (rect == null) {
        throw new NullPointerException("need rect parameter");
    }
    addRoundRect(rect.left, rect.top, rect.right, rect.bottom, radii, dir);
}
RectF rectF = new RectF(200, 200, 500, 700);
RectF rectF5 = new RectF(200, 800, 700, 1200);
mPath.addRect(rectF, Path.Direction.CCW);
mPath.addRoundRect(rectF5, 20,20,Path.Direction.CCW);

8.1 定制四个角的弧度

addRoundRect(RectF rect, float[] radii, Direction dir)可以定制矩形四个角的弧度。

float[] radii:需要传入8个数值,分四组,分别对应每个角所使用的椭圆的横轴半径和纵轴半径,从左上角开始。

RectF rectF3 = new RectF(200,200,900,700);
float[] radii = {70, 70, 70, 30, 30, 70, 70, 0};
mPath.addRoundRect(rectF3,radii, Path.Direction.CW);

9.曲线 quadTo cubicTo

quadTo ,cubicTo用来实现贝塞尔曲线,quadTo有一个控制点,cubicTo有两个控制点。

Path path = new Path();
path.moveTo(200, 200);
path.quadTo(300, 700, 800, 500);
canvas.drawPath(path, mPaint2);