精通Android自定义View(十)绘制篇Canvas分析之绘制Path

1 Path常用方法简析

Path在2D绘图中是一个很重要的类。

Path在这里可以绘制基本的图形,也可以绘制其他复杂的图形。

 

2 常用API解析与示例

2.1 xxxTo方法

Path类中提供了一套xxxTo方法,其作用是从起点到终点移动path画笔并绘制线(moveTo方法只移动path画笔不绘制线),线有直线和曲线

方法汇总简述
方法名参数解析
lineTo(float x, float y)绘制直线,x:终点x坐标值,y:终点y坐标值
moveTo(float x, float y)移动画笔,x:终点x坐标值,y:终点y坐标值
arcTo(RectF oval, float startAngle, float sweepAngle)绘制圆弧,oval:圆弧矩形区域,startAngle:起始角度,sweepAngle:圆弧旋转的角度
arcTo(RectF oval, float startAngle, float sweepAngle,boolean forceMoveTo)绘制圆弧,oval:圆弧矩形区域,startAngle:起始角度,sweepAngle:圆弧旋转的角度,forceMoveTo:是否在绘制圆弧前移动(moveTo)path画笔位置
arcTo(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean forceMoveTo)绘制圆弧,left、top、right、bottom组成圆弧矩形区域,startAngle:起始角度,sweepAngle:圆弧旋转的角度,forceMoveTo:是否在绘制圆弧前移动(moveTo)path画笔位置
quadTo(float x1, float y1, float x2, float y2)绘制二阶贝塞尔曲线,控制点坐标:(x1,y1),终点坐标:(x2,y2)
cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)绘制三阶贝塞尔曲线,其中控制点1坐标为(x1,y1),控制点2坐标为(x2,y2),终点坐标为(x3,y3)

2.1.1 lineTo(float x, float y)

绘制直线,从当前画笔位置出发,连接终点(x,y)

mPath.lineTo(300,300);
canvas.drawPath(mPath,mMPaint);

2.1.2 moveTo(float x, float y)

移动画笔,从当前画笔位置移动到终点(x,y)

        mPath.moveTo(100,100);
        mPath.lineTo(300,300);
        canvas.drawPath(mPath,mMPaint);

2.1.3 arcTo(RectF oval, float startAngle, float sweepAngle)

绘制圆弧,从当前画笔位置出发,连线到内切矩形区域oval的圆弧的起始角度startAngle位置(X轴正方向为0°),顺时针旋转绘制圆弧,旋转度数为sweepAngle(sweepAngle为负时则逆时针旋转)

        RectF rectF = new RectF(100,100,300,400);
        //绘制上述矩形区域
        canvas.drawRect(rectF,mMPaint2);
        //创建路径
        mPath.arcTo(rectF,0,180);
        //绘制路径
        canvas.drawPath(mPath,mMPaint);

2.1.4 arcTo(RectF oval, float startAngle, float sweepAngle,boolean forceMoveTo)

绘制圆弧,若forceMoveTo为false,则用法和2.1.3 中 arcTo(RectF oval, float startAngle, float sweepAngle)一样,绘制圆弧之前不会移动(moveTo)path画笔位置。若为true,先强制调用moveTo移动path画笔至圆弧起点,再绘制圆弧。ps:如果调用arcTo(RectF oval, float startAngle, float sweepAngle,boolean forceMoveTo)方法之前没有对path进行任何操作,则forceMoveTo设置true或false效果都和设置true一样

        RectF rectF = new RectF(100,100,300,400);
        //绘制上述矩形区域
        canvas.drawRect(rectF,mMPaint2);
        mPath.moveTo(100,100);
        mPath.arcTo(rectF,0,180,false);
        mPath.close();
        canvas.drawPath(mPath,mMPaint);

//修改为true后
//先强制调用moveTo移动path画笔至圆弧起点,再绘制圆弧
 mPath.arcTo(rectF,0,180,true);

2.1.5 quadTo(float x1, float y1, float x2, float y2) 二阶贝塞尔曲线

从path画笔当前位置出发,以(x₁,y₁)为控制点,向终点(x₂,y₂)绘制一条二阶贝塞尔曲线

        mPath.moveTo(100,100);
        mPath.quadTo(200,0,400,100);
        canvas.drawPath(mPath,mMPaint);

 

2.1.6 cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)三阶贝塞尔曲线

从path画笔当前位置出发,以(x1,y1)为控制点1,以(x2,y2)为控制点2,向终点(x3,y3)绘制一条三阶贝塞尔曲线

       mPath.moveTo(100,100);
        mPath.cubicTo(200,0,300,90,500,100);
        canvas.drawPath(mPath,mMPaint);


2.2 rXxxTo方法

rXxxTo方法的r意思是relative,即相对的意思,方法有四个,如下所示,其功能与对应的xxxTo方法一样,区别在于rXxxTo方法在绘制Path时是以当前path画笔位置为坐标原点,即相对于path画笔位置进行绘制,而xxxTo方法的坐标原点则与当前canvas坐标原点一致

案例分析绘制区别

path.moveTo(100,100);
path.lineTo(300,300);
canvas.drawPath(path, pathPaint);

上述代码是从(100,100)到(300,300)绘制一条直线,那么如果用rXxxTo方法

path.moveTo(100,100);
path.rLineTo(300,300);
canvas.drawPath(path, pathPaint);

rXxxTo方法,相当于是从点(100,100)到点(100+300,100+300)绘制了一条直线


2.3 addXxx方法

Path类中还提供了一套addXxx方法,字面理解就是添加一段相应的线,线可以是曲线、完整的圆形、矩形等,甚至可以是另一组Path的线。所谓添加的意思,可以理解为在绘制这段线前,移动(moveTo)path画笔位置到线的起始位置,然后再绘制线,也就是说添加的这段线,与之前绘制的Path是分离的(除非后绘制的这段线的起始点与之前Path的终点一致),方法还是比较多的,如下:

方法名参数解析
addArc(RectF oval, float startAngle, float sweepAngle)添加圆弧oval:圆弧矩形区域,startAngle:起始角度,sweepAngle:圆弧旋转的角度
addArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle)添加圆弧left、top、right、bottom组成圆弧矩形区域,startAngle:起始角度,sweepAngle:圆弧旋转的角度。ps:此方法在API 19以上有效
addCircle(float x, float y, float radius, Direction dir)添加圆形x:圆形圆心的x坐标,y:圆形圆心的y坐标,radius:圆形半径,dir:线的闭合方向(CW顺时针方向 | CCW逆时针方向)
addOval(RectF oval, Direction dir)添加椭圆oval:椭圆内切的矩形区域,dir:线的闭合方向(CW顺时针方向 | CCW逆时针方向)
addOval(float left, float top, float right, float bottom, Direction dir)添加椭圆left、top、right、bottom组成椭圆内切的矩形区域,dir:线的闭合方向(CW顺时针方向 | CCW逆时针方向)
addRect(RectF rect, Direction dir)添加矩形rect:矩形区域,dir:线的闭合方向(CW顺时针方向 | CCW逆时针方向)
addRect(float left, float top, float right, float bottom, Direction dir)添加矩形left、top、right、bottom组成矩形区域,dir:线的闭合方向(CW顺时针方向 | CCW逆时针方向)
addRoundRect(RectF rect, float rx, float ry, Direction dir)添加统一圆角的圆角矩形rect:矩形区域,rx:椭圆圆角的横轴半径,ry:椭圆圆角的纵轴半径,dir:线的闭合方向(CW顺时针方向 | CCW逆时针方向)
addRoundRect(float left, float top, float right, float bottom, float rx, float ry,Direction dir)添加统一圆角的圆角矩形left、top、right、bottom组成矩形区域,rx:椭圆圆角的横轴半径,ry:椭圆圆角的纵轴半径,dir:线的闭合方向(CW顺时针方向 | CCW逆时针方向)
addRoundRect(RectF rect, float[] radii, Direction dir)添加非统一圆角的圆角矩形rect:矩形区域,radii:矩形四个椭圆圆角的横轴半径和纵轴半径的数组,一共8个数值,dir:线的闭合方向(CW顺时针方向 | CCW逆时针方向)
addRoundRect(float left, float top, float right, float bottom, float[] radii,Direction dir)添加非统一圆角的圆角矩形left、top、right、bottom组成矩形区域,radii:矩形四个椭圆圆角的横轴半径和纵轴半径的数组,一共8个数值,dir:线的闭合方向(CW顺时针方向 | CCW逆时针方向)
addPath(Path src)添加一组Pathsrc:要添加的Path
addPath(Path src, float dx, float dy)添加一组平移后的Pathsrc:要添加的Path,dx:平移的x坐标,dy:平移的y坐标
addPath(Path src, Matrix matrix)添加一组经过矩阵变换后的Pathsrc:要添加的Path,matrix:3x3的矩阵

 

2.3.1 addArc(RectF oval, float startAngle, float sweepAngle)

addArc两个方法使用起来与arcTo(RectF oval, float startAngle, float sweepAngle,boolean forceMoveTo)forceMoveTo设置为true效果一致

//代码块一 
        mPath.moveTo(100,100);
        mPath.cubicTo(200,0,300,150,500,100);
        //添加
        RectF rectF = new RectF(100,100,300,400);
        mPath.addArc(rectF,0,180);
        canvas.drawPath(mPath,mMPaint);

//代码块二

        mPath.moveTo(100,100);
        mPath.cubicTo(200,0,300,150,500,100);
        //添加
        RectF rectF = new RectF(100,100,300,400);
        mPath.arcTo(rectF,0,180,true);
        canvas.drawPath(mPath,mMPaint);

//上述两种写法 效果一至

2.3.2 addCircle(float x, float y, float radius, Direction dir)

以点(x,y)为圆心,添加一个半径长为radius的圆形,绘制起始角度为0°(x轴方向),绘制方向通过dir的值而定,dir为CW时顺时针绘制,dir为CCW时逆时针绘制

        //顺时针绘制
        mPath.addCircle(200,150,100, Path.Direction.CW);
        canvas.drawPath(mPath,mMPaint);

2.3.2 addOval(RectF oval, Direction dir)

在oval矩形区域中,添加一个内切的椭圆,绘制起始角度为0°(x轴方向),绘制方向通过dir的值而定,dir为CW时顺时针绘制,dir为CCW时逆时针绘制

        RectF rectF = new RectF(100,100,400,250);
        //绘制矩形
        canvas.drawRect(rectF,mMPaint2);

        //创建路径
        mPath.addOval(rectF, Path.Direction.CW);
        //绘制路径
        canvas.drawPath(mPath,mMPaint);

 

2.3.3 addRect(RectF rect, Direction dir)

添加一个区域为rect的矩形,绘制起点为左上角,绘制方向通过dir的值而定,dir为CW时顺时针绘制,dir为CCW时逆时针绘制

2.3.4 addRoundRect(RectF rect, float rx, float ry, Direction dir)

添加一个区域为rect的圆角矩形,四个角的圆角大小一致,圆角的横轴半径为rx,纵轴半径为ry,dir为CW时顺时针绘制,绘制起点为左下角,dir为CCW时逆时针绘制,绘制起点为左上角(注意对比顺时针和逆时针的绘制起点)

2.3.5 addRoundRect(RectF rect, float[] radii, Direction dir)

添加一个区域为rect的圆角矩形,四个角的圆角的横轴和纵轴半径由radii数组中的8个数值决定,dir为CW时顺时针绘制,绘制起点为左下角,dir为CCW时逆时针绘制,绘制起点为左上角(注意对比顺时针和逆时针的绘制起点)

需要注意的是,如果radii数组中的元素小于8,系统会抛出错误信息radii[] needs 8 values

2.3.6 addPath(Path src)

添加一组path路径

2.3.7addPath(Path src, float dx, float dy)

添加一组path路径,然后将其进行平移,x轴上的平移距离为dx,y轴上的平移距离为dy

2.3.8 addPath(Path src, Matrix matrix)

添加一组名为src的Path副本,然后将其进行矩阵变换,矩阵为matrix(3x3的矩阵)


3 填充模式

方法名参数解析
setFillType(FillType ft)设置Path的填充模式ft:填充类型,有EVEN_ODDINVERSE_EVEN_ODDWINDINGINVERSE_WINDING 四种模式
getFillType()获取当前Path的填充模式
isInverseFillType()判断当前Path填充模式是否是反向规则(INVERSE_XXX)
toggleInverseFillType()当前Path的填充模式与其反向规则模式进行相互切换

 

 

4 其他方法

方法名参数解析
close()封闭当前Path,连接起点终点
reset()清空Path中的所有直线和曲线,保留填充模式设置,不保留Path上相关的数据结构
rewind()清空Path中的所有直线和曲线,不保留填充模式设置,但会保留Path上相关的数据结构,以便高效地复用
set(Path src)用名为src的Path替换当前的Path
op(Path path, Op op)当前Path名为path的Path进行布尔运算(取差集、交集、并集等)op:运算逻辑,有DIFFERENCE(差集)REVERSE_DIFFERENCE(差集)INTERSECT(交集)UNION(并集)XOR(异或)五种运算逻辑可选。ps:此方法在API 19以上有效
offset(float dx, float dy)平移当前Pathx轴上平移的距离为dxy轴上平移的距离为dy
offset(float dx, float dy, Path dst)平移名为dst的Pathx轴上平移的距离为dxy轴上平移的距离为dy
transform(Matrix matrix)对当前Path进行矩阵变换,矩阵为matrix(3x3矩阵)
transform(Matrix matrix, Path dst)对名为dst的Path进行矩阵变换,矩阵为matrix(3x3矩阵)
setLastPoint(float dx, float dy)设置终点,设置当前Path最后一个点的位置为(dx,dy)
isEmpty()判断当前Path是否为空
isConvex()判断当前Path围成的图形是否凸多边形。ps:此方法在API 21以上有效
isRect(RectF rect)判断当前Path是否为矩形,如是,则将当前Path存储到新建的rect中


 

早起的年轻人 CSDN认证博客专家 移动开发 项目管理 Java
只要用心去做,每一件事情还是有可能成功的,当然成功是没有界限的,只不过是达到自己心里的那个目标,公众号:我的大前端生涯,一个爱喝茶的程序员,通常会搞搞SpringBoot 、Herbinate、Mybatiys、Android、iOS、Flutter、Vue、小程序等.
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页