View 自定义 - 路径 Path

参考文章

一、概念

用于描述顺序 & 区域,单使用无法产生效果。

图形绘制的本质是先画点再将点连接起来,所以点与点之间是存在一个先后顺序的。图形的方向影响的是:添加图形时确定闭合顺序(各个点的记录顺序)、图形的渲染结果(是判断图形渲染的重要条件)。 

1.1 开放路径、闭合路径

View 自定义 - 路径 Path_第1张图片

1.2 判断点在图形内还是图形外

1.2.1 奇偶规则

从任意位置 P 作一条射线,对与图形的边相交的点数进行判断:

  • 若相交的点数为奇数,则认为 P 为图形内的点。
  • 若相交的点数为偶数,则认为 P 为图形外的点。

View 自定义 - 路径 Path_第2张图片

1.2.2 非零环绕数规则

从任意位置 P 作一条射线,当 P 点沿射线方向移动时,对在每个方向上穿过射线的边进行判断:

  • 每当图形的边从右到左穿过射线时 +1,从左到右穿过时 -1。
  • 若环绕数为 ≠0 则 P 为图形内的点,否则为图形外的点。

View 自定义 - 路径 Path_第3张图片

二、创建对象

Path 的起点坐标默认为 (0,0)。

  •  建全局 Path 对象,在 onDraw() 中按需修改,不要在 onDraw() 里创建,因为若 View 频繁刷新,就会频繁创建对象,拖慢刷新速度。
val path = Path()

三、设置路径

moveTo()

public void moveTo(float x, float y)

移动到新的坐标点,并作为新的起点,影响后续路径绘制。

setLastPoint()

public void setLastPoint(float dx, float dy)

移动到新的坐标点,不会改变原有起点,不影响后续路径绘制。

lineTo()

public void lineTo(float x, float y)

移动到新的坐标点,且和之前的点用直线连接。

close()

public void close()

闭合路径,将当前点和起始点连接。如果终点和起点连接后仍无法形成封闭图形则什么也不做。

先 lineTo(400,500),分别使用 moveTo(300,300) 和 setLastPoint(300,300),再 lineTo(900,800),最后 close()。 

View 自定义 - 路径 Path_第4张图片

四、重置路径

FillType 影响显示效果,数据结构影响重建速度,一般选择 reset()。

reset()

public void reset()

保留 FillType 设置,不保留原有数据结构。

rewind()

public void rewind()

不保留 FillType 设置,保留原有数据结构。

五、添加路径

5.1 基本图形

添加图形路径后会改变路径的起点。形参 dir 即方向 direction,指定绘制时是顺时针还是逆时针(CW为顺时针clockwise,CCW为逆时针counter-clockwise)。

addRect()

矩形

addRect(RectF rect, Path.Direction dir)

路径起点变为矩形的左上角顶点。

addRoundRect()

圆角矩形

addRoundRect(RectF rect, float rx, float ry, Path.Direction dir)

addArc()

arcTo()

圆弧

public void addArc (RectF oval, float startAngle, float sweepAngle)

startAngle起始角度,sweepAngle绘制扫过的角度。

public void arcTo (RectF oval, float startAngle, float sweepAngle)

与上面方法唯一不同的是:如果圆弧的起点和最后一个坐标点不相同,就连接两个点。

public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)

forceMoveTo:是否将之前路径的结束点设置为圆弧起点。设为 true 在新的起点画圆弧,不连接最后一个点与圆弧起点,即与之前路径没有交集(同第一个)。设为 false 在新的起点画圆弧,但会连接之前路径的结束点与圆弧起点,即与之前路径有交集(同第二个)。

addCircle()

圆形

addCircle(float x, float y, float radius, Path.Direction dir)

路径起点变为圆在X轴正方向最大的点 。

addOval()

椭圆形

addOval(RectF oval, Path.Direction dir) 

oval作为椭圆的外切矩形区域。

canvas.translate(350, 500)    //为了方便观察,平移坐标系
path.addRect(0, 0, 400, 400, Path.Direction.CW)    //顺时针
//path.addRect(0,0,400,400, Path.Direction.CCW)    //逆时针
canvas.drawPath(path paint)

View 自定义 - 路径 Path_第5张图片

5.1.1 矩形 addRect()

addRect(RectF rect, Path.Direction dir)

路径起点变为矩形的左上角顶点。

5.1.2 圆角矩形 addRoundRect()

addRoundRect(RectF rect, float rx, float ry, Path.Direction dir)

5.1.3 圆弧 addArc()、arcTo()

public void addArc (RectF oval, float startAngle, float sweepAngle)

startAngle起始角度,sweepAngle绘制扫过的角度。

public void arcTo (RectF oval, float startAngle, float sweepAngle)

与上面方法唯一不同的是:如果圆弧的起点和最后一个坐标点不相同,就连接两个点。

public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)

forceMoveTo:是否将之前路径的结束点设置为圆弧起点。设为 true 在新的起点画圆弧,不连接最后一个点与圆弧起点,即与之前路径没有交集(同第一个)。设为 false 在新的起点画圆弧,但会连接之前路径的结束点与圆弧起点,即与之前路径有交集(同第二个)。

5.1.4 圆形 addCircle()

addCircle(float x, float y, float radius, Path.Direction dir)

路径起点变为圆在X轴正方向最大的点 。

5.1.5 椭圆形 addOval()

addOval(RectF oval, Path.Direction dir) 

oval作为椭圆的外切矩形区域。

5.2 合并路径 addPath()

public void addPath(Path src)
public void addPath(Path src, Matrix matrix)
public void addPath(Path src, float dx, float dy)

将 src 路径添加过来,dx dy 是先偏移后再添加,matrix 是先变换后再添加。

View 自定义 - 路径 Path_第6张图片

canvas.translate(350, 500)    //为了方便观察,平移坐标系
Path pathRect = new Path()
Path  pathCircle = new Path()
pathRect.addRect(-200, -200, 200, 200, Path.Direction.CW)    // 画一个矩形路径
pathCircle.addCircle(0, 0, 100, Path.Direction.CW)    // 画一个圆形路径
pathRect.addPath(pathCircle, 0, 200)    // 将圆形路径移动(0,200),再添加到矩形路径里
canvas.drawPath(pathRect,mPaint1)    // 绘制合并后的路径

六、判断路径属性

6.1 内容是否为空 isEmpty()

public boolean isEmpty()

判断path中是否包含内容。

6.2 是否为矩形 isRect()

public boolean isRect(RectF rect)

判断path是否是一个矩形,如果是的话会将矩形信息方巾参数 rect 中。

path.lineTo(0,400)
path.lineTo(400,400)
path.lineTo(400,0)
path.lineTo(0,0)
RectF rect = new RectF()
boolean b = path.isRect(rect)    //true
//rect存放信息:
//rect.left = 0
//rect.top = 0
//rect.right = 400
//rect.bottom = 400

6.3 替换 set()

public void set(Path src)

用 src 路径替代现有路径。

path1.addRect(-200,-200,200,200, Path.Direction.CW)    //矩形路径
path2.addCircle(0,0,100, Path.Direction.CW)    //圆形路径
path1.set(path2)    //将圆形路径代替矩形路径

6.4 偏移 offset()

public void offset(float dx, float dy)

public void offset(float dx, float dy, Path dst)

偏移位置,dst 用来存储平移后的路径状态但不影响当前路径。

View 自定义 - 路径 Path_第7张图片

canvas.translate(350, 500)    //为了方便观察,平移坐标系
//path中添加一个圆形(圆心在坐标原点)
path = new Path()
path.addCircle(0, 0, 100, Path.Direction.CW)
//平移路径并存储平移后的状态
Path dst = new Path()
path.offset(400, 0, dst)    //平移
canvas.drawPath(path, mPaint1)    //绘制path
//通过dst绘制平移后的图形(红色)
mPaint1.setColor(Color.RED)
canvas.drawPath(dst,mPaint1)

七、设置路径填充颜色 xxxFillType()

View 自定义 - 路径 Path_第8张图片

getFillType()

public FillType getFillType()

设置填充规则。

setFillType()

public void setFillType(FillType ft)

获取当前填充规则。

isInverseFillType()

public boolean isInverseFillType()

判断是否是反向(INVERSE)规则。

toggleInverseFillType()

public void toggleInverseFillType()

切换填充规则(即原有规则与反向规则之间相互切换)。

  • 奇偶规则:EVEN_ODD
  • 反奇偶规则:INVERSE_EVEN_ODD
  • 非零环绕数规则:WINDING
  • 反非零环绕数规则:INVERSE_WINDING

View 自定义 - 路径 Path_第9张图片

canvas.translate(350, 500)    //为了方便观察,平移坐标系
path.addRect(-200, -200, 200, 200, Path.Direction.CW)    // 在Path中添加一个矩形
path.setFillType(Path.FillType.EVEN_ODD)    //设置Path填充模式为 奇偶规则
// path.setFillType(Path.FillType.INVERSE_EVEN_ODD)    // 反奇偶规则
canvas.drawPath(path, mPaint1)    //画出路径

 八、布尔操作 op()

View 自定义 - 路径 Path_第10张图片

 两个 Path 之间的运算,用简单的图形通过特定规则合成相对复杂的图形。

public boolean op(Path path, Op op)

对调用的 path 和传入的 path 执行布尔运算,运算结果存入到调用该方法的 path 中。

public boolean op(Path path1, Path path2, Op op)

对 path1 和 path2 执行布尔运算,运算结果存入到调用该方法的 path 中。

View 自定义 - 路径 Path_第11张图片

canvas.translate(550, 550)    //为了方便观察,平移坐标系
//画两个圆
path1.addCircle(0, 0, 100, Path.Direction.CW)
path2.addCircle(50, 0,100, Path.Direction.CW)
//取两个路径的异或集
path1.op(path2, Path.Op.XOR)
//画出路径
canvas.drawPath(path1, mPaint1)

九、贝塞尔曲线

参考文章

计算曲线的数学公式,任何一条曲线都可以用贝塞尔曲线表示。

数据点 指路径的起始点和终止点。
控制点 决定了路径的弯曲轨迹。根据控制点的个数,贝塞尔曲线被分为一阶贝塞尔曲线(0个控制点)、二阶贝塞尔曲线(1个控制点)、三阶贝塞尔曲线(2个控制点)等等。n+1阶贝塞尔曲线 = 有n个控制点。(1阶 = 一条直线,高阶可以拆解为多条低阶曲线)
绘制二阶贝赛尔曲线

public void quadTo(float x1, float y1, float x2, float y2)

(x1,y1)为控制点,(x2,y2)为终点

public void rQuadTo(float dx1, float dy1, float dx2, float dy2)

(x1,y1)为控制点距离起点的偏移量,(x2,y2)为终点距离起点的偏移量

绘制三阶贝赛尔曲线

public void conicTo(float x1, float y1, float x2, float y2, float weight)

(x1,y1),(x2,y2)为控制点,(x3,y3)为终点

public void rConicTo(float dx1, float dy1, float dx2, float dy2, float weight)

(x1,y1),(x2,y2)为控制点距离起点的偏移量,(x3,y3)为终点距离起点的偏移量

你可能感兴趣的:(View,android)