Android开发 - Canvas中Path绘制路径的详解与使用

Path回顾

  • Path类封装复合(多轮廓)几何路径由直线段二次曲线三次曲线组成。它可以用画布绘制:canvas.drawPath(path, paint)填充或笔划(基于绘画的样式),或者可以用于剪裁或绘制路径上的文本Path既是路径,路径走多了就变成一种套路,只要我们会解套,那这种套路就是高速公路。路径走完形成闭环,最终形成一个图形

paint回顾

  • path的合作伙伴是Paintpath设置好路径而绘制由paint完成绘制,paint既是画笔以下是基本的画笔设置

    paint.setAntiAlias(true);
    // 设置画笔的风格style (Paint.Style.FILL填充,Paint.Style.STROKE描边    Paint.Style.FILL_AND_STROKE填充加描边)
    paint.setStyle(Paint.Style.STROKE);
    // 设置画笔的颜色
    paint.setColor(Color.RED);
    //设置描边宽度
    paint.setStrokeWidth(10f);
    

    关于setStyle注意点

    1. 不同的填充效果不一样,比如绘制路径,三个点描边出来的效果却是两条线,而填充模式下是一个三角形的色块

      • paint.setStyle(Paint.Style.STROKE);

      • paint.setStyle(Paint.Style.FILL);

    2. Paint.Style.FILL_AND_STROKE;:会出现角有缺陷,在使用需要注意

Path的使用

构造path对象

构造空对象

public void path()

拷贝一个对象

public void path(Path src)

Path的方法

改变起始坐标:moveTo

  • moveTo(x,y);

    • 参数解析
      • x:设置新的x轴坐标,默认为0
      • y:设置新的y轴坐标,默认为0
  • path默认的起始坐标是0,0;如果想改变起始坐标位置,通过moveTo来设置

    private void moveTo(Path path) {
        path.moveTo(x, y);
        path.lineTo(0, 1000);
        path.lineTo(500, 500);
    }
    
    

  • 图一是没设置moveTo(0,0)默认(0,0)坐标图二moveTo(100,100)新的坐标点,起始点为A,因此只变更A的位置

画点成线:rLineTo、lineTo

  • rLineTo(x,y);

    • 参数解析
      • x如果没有上一个连接点,则从0开始计算放置新的x轴坐标连接点;否则从上一个(y点)连接点开始计算放置新的x轴坐标连接点
      • y以上一个连接点(x点)为起点计算放置新的y轴坐标连接点
  • lineTo(x,y);

    • 参数解析
      • x从0开始放置新的x轴坐标连接点
      • y从0开始放置新的y轴坐标连接点
  • lineTo相同,但坐标被视为相对于最后一个此轮廓上的点。如果没有上一个点,则将moveTo(0,0)将自动插入

  • rLineTolineTo的区别:

    • rLineTo:从A点B点C点的坐标以B点为起点,也就是后一个点以前一个坐标为起点

      private void rLineTo(Path path) {
          path.rLineTo(0, 1000);	//A(x):0; B(y):1000
          path.rLineTo(1000, 0);	//c(x):1000; y:0
      }
      

    • lineTo起点为0,直接将ABC三个点按顺序在view中连起来

      private void lineTo(Path path) {
          path.lineTo(0, 1000);	//A(x):0; B(y):1000
          path.lineTo(1000, 0);	//c(x):1000; y:0
      }
      

贝塞尔曲线:quadTo

  • quadTo(float x1, float y1, float x2, float y2);

    • 参数解析
      • x1:决定曲线弯曲朝向
      • y1:决定曲线弯曲高度
      • x2:决定起始点的横向位置
      • y2:决定起始点的纵向位置
  • 注意事项:在quadTo方法调用时moveTo方法也会进行调整:

    • moveTo(x, y)调整后参数解析
      • x:决定终点的横向位置
      • y:决定终点的纵向位置
  • 代码解析

     private void quadTo(Path path) {
        path.moveTo(100, 0);	//决定起始点位置
        path.quadTo(100, 1000, 1000, 0);	//决定弯曲朝向、弯曲高度、终点位置
    }
    

贝塞尔曲线:rQuadTo

  • quadTo基本相同,唯一的变化是moveTo的作用变为移动曲线整体位置。如果多画一个曲线,那么第二个rQuadTo画出来的曲线起始点将连接上一个曲线的终点。如果没有上一个点,则将moveTo(0, 0)将自动插入。

立方贝塞尔:cubicTo

  • cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)

    • 参数解析
      • x1:决定曲线的弯曲朝向
      • y1:决定曲线的弯曲高度
      • x2:决定曲线的弯曲朝向
      • y2:决定曲线的弯曲高度
      • x3:决定终点的横向位置
      • y3:决定终点的纵向位置
  • 代码实例

    private void cubicTo(Path path) {
    
        int width=getWindowSize(0)/2;
        int height=getWindowSize(1)/2;
    
        path.cubicTo(0, height, width, height, width, 0);
    }
    

圆弧:arcTo

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

    • 参数解析
      • oval:调用RectF对象决定扇形图形view大小
      • startAngle:开始角度
      • sweepAngle:扇形角度
      • forceMoveTo:如果为true,则始终以圆弧开始新轮廓
  • 代码实例

    private void arcTo(Path path) {
        paint.setStyle(Paint.Style.FILL);
        RectF rectF=new RectF(0,0,500,500);
        path.arcTo(rectF,startAngle,sweepAngle,true);
    
    }
    

关闭当前轮廓 :close

  • close():关闭当前轮廓。如果当前点不等于轮廓的第一个点,自动添加线段与第一个点连接。

  • 没有调用close()之前三个点确定两条线

    private void close(Path path) {
        path.moveTo(100,100);
        path.lineTo(100,500);
        path.lineTo(500,500);
    }
    

  • 调用close()三个点首尾相连

    private void close(Path path) {
        path.moveTo(100,100);
        path.lineTo(100,500);
        path.lineTo(500,500);
        path.close();
    }
    

将闭合矩形轮廓添加到路径:addRect

  • addRect(RectF rect, Direction dir)

    • 参数解析
      • rect:调用RectF对象确定矩形区域
      • dir:绘制矩形轮廓的方向Path.DirectionCW顺时针Path.Direction.CCW逆时针
  • 代码实例

    private void addRect(Path path)
    {
        RectF rect=new RectF(0,0,500,500);
        path.addRect(rect, Path.Direction.CCW);
    
    }
    

绘制椭圆:addOval

  • addOval(RectF oval, Direction dir)

    • 参数解析:
      • oval:调用RectF对象确定椭圆区域
      • dir:绘制椭圆轮廓的方向Path.DirectionCW顺时针Path.Direction.CCW逆时针
  • 代码实例

    private void addOval(Path path) {
        RectF rect = new RectF(0, 0, 1000, 500);	//如果后2个参数相等绘制出来的图形就是圆形
        path.addOval(rect, Path.Direction.CCW);
    }
    

绘制圆形:addCircle

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

    • 参数解析
      • x:圆心的x轴坐标位置
      • y:圆心的y轴坐标位置
      • radius:半径
      • dir:绘制圆形轮廓的方向Path.DirectionCW顺时针Path.Direction.CCW逆时针
  • 代码实例

    private void addCircle(Path path) {
        path.addCircle(100, 100, 100, Path.Direction.CCW);
    }
    

在矩形区域画弧度:addArc

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

    • 参数解析
      • oval:调用RectF对象确定圆弧区域
      • startAngle:开始角度
      • sweepAngle:扇形角度
  • 代码实例

    private void addArc(Path path) {
        paint.setStyle(Paint.Style.STROKE);	//设置画笔空心
        RectF rect = new RectF(0, 0, 500, 500);	//如果这个矩形是一个正方形,那么画出来的弧度是半圆形,否则是一个不规则的股弧度(1000,500就是压扁发半圆,500,1000就是瘦高的半圆)
        path.addArc(rect,0,180);
    }
    

    private void addArc(Path path) {
        paint.setStyle(Paint.Style.FILL);	//设置画笔实心
        RectF rect = new RectF(0, 0, 500, 500);
        path.addArc(rect,0,180);
    }
    

在矩形中绘制一个圆形/圆角矩形:addRoundRect

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

    • 参数解析
      • rect:调用RectF对象确定矩形区域
      • rx:决定圆角角度的x轴坐标位置
      • ry:决定圆角角度的y轴坐标位置
      • dir:绘制圆形轮廓的方向Path.DirectionCW顺时针Path.Direction.CCW逆时针
  • 代码实例

    private void addRoundRect(Path path) {
        RectF rect = new RectF(0, 0, 1000, 500);
        path.addRoundRect(rect,rect.right/2,rect.bottom/2, Path.Direction.CW);
    }
    

    private void addRoundRect(Path path) {
        RectF rect = new RectF(0, 0, 1000, 500);
        path.addRoundRect(rect,100,100, Path.Direction.CW);
    }
    

  • 注意点

    • rx或者ry只要有一个角的坐标为0都无法生效
    • rx和ry其实可以看成各个顶点的角度百分比

在路径中添加新的路径(子路径):addPath

三种方式

  1. addPath(Path src, Matrix matrix)
  2. addPath(Path src)
  3. addPath(Path src, float dx, float dy)

参数解析

  • src:可以将已存在的path路径进行拷贝

  • matrix:指定子路径在父路径中的坐标位置,通常需要先实例化matrix后进行matrix.setTranslate(x, y);设置好坐标后进行传入。如果没有,默认覆盖在父路径的view上

  • dx:指定子路径在父路径中的x轴坐标位置。如果没有,默认覆盖在父路径view的x轴坐标上

  • dy:指定子路径在父路径中的y轴坐标位置。如果没有,默认覆盖在父路径view的y轴坐标上

代码实例

private void addPath(Path path) {
    RectF rect = new RectF(0, 0, 1000, 500);
    path.addRoundRect(rect,0,0, Path.Direction.CW);

    Path path1=new Path(path);
    Matrix matrix=new Matrix();
    matrix.setTranslate(200,200);

    path.addPath(path1,100,100);
    path.addPath(path1,matrix);
}

整体偏移 :offset

  • offset(x, y)

    • 参数解析
      • x:需要偏移到的x轴坐标位置
      • y:需要偏移到的y轴坐标位置
  • offset是针对整个path来完成偏移的,而不是某个点,与MoveTo不同MoveTo是针对起始坐标点

  • 起始坐标是(0,0),通过偏移来完成整体的下移

    private void offset(Path path) {
        RectF rect = new RectF(0, 0, 1000, 500);
        path.addRoundRect(rect,0,0, Path.Direction.CW);
    
        path.offset(100,100);
    }
    

重置最后一个点的位置:setLastPoint

  • setLastPoint(x, y)

    • 参数解析
      • x:设置最后一个点的x轴坐标位置
      • y:设置最后一个点的y轴坐标位置
  • 代码实例

    • 正常如下

      private void setLastPoint(Path path) {
          path.lineTo(0,1000);
          path.lineTo(500,1000);
      //  path.setLastPoint(500,500);
      }
      

    • setLastPoint

      private void setLastPoint(Path path) {
          path.setLastPoint(500,500);
      }
      

路径坐标控制:transform

  • transform(x, y)

    • 参数解析
      • x:设置x轴坐标位置
      • y:设置y轴坐标位置
  • 通过Matrix对path的路径进行控制和操控,比offset更灵活,只要操作Matrix即可完成当前path的变种

    Matrix matrix = new Matrix();
    matrix.setTranslate(0, 0);
    path.transform(matrix);
    canvas.drawPath(path, paint);
    

双路径区域处理:OP

  • path的op()方法是把自身和另外一条path中的区域做相应的处理

  • 代码实例

    Path path1 = new Path();
    path1.addCircle(150, 150, 100, Path.Direction.CW);
    
    Path path2 = new Path();
    path2.addCircle(200, 200, 100, Path.Direction.CW);
    
    path1.op(path2, Path.Op.DIFFERENCE);	//OP处理
    canvas.drawPath(path1, mPaint);
    
  • 传入参数有5种模式

    • DIFFERENCE:减去Path2Path1区域剩下的部分

    • INTERSECT:保留Path2Path1共同的部分

    • UNION:保留Path1Path2

    • XOR:保留Path1Path2去除共同的部分

    • REVERSE_DIFFERENCE :减去Path1Path2区域剩下的部分

计算边界:computeBounds

  • computeBounds (RectF bounds, boolean exact)

    • bounds:调用RectF对象创建一个矩形区域,测量结果会放入这个矩形
    • exact是否精确测量,目前这一个参数作用已经废弃,一般写true即可
  • 代码实例

    // 移动canvas,mViewWidth与mViewHeight在 onSizeChanged 方法中获得
    canvas.translate(mViewWidth/2,mViewHeight/2);
    
    RectF rect1 = new RectF();              // 存放测量结果的矩形
    
    Path path = new Path();                 // 创建Path并添加一些内容
    path.lineTo(100,-50);
    path.lineTo(100,50);
    path.close();
    path.addCircle(-100,0,100, Path.Direction.CW);
    
    path.computeBounds(rect1,true);         // 测量Path
    
    canvas.drawPath(path,mDeafultPaint);    // 绘制Path
    
    mDeafultPaint.setStyle(Paint.Style.STROKE);
    mDeafultPaint.setColor(Color.RED);
    canvas.drawRect(rect1,mDeafultPaint);   // 绘制边界
    

重置路径:reset、rewind

  • 重置Path有两个方法,分别是reset()rewind(),两者区别主要有以下两点

    • reset:保留FillType设置,不保留原有数据结构
    • rewind:不保留FillType设置,保留原有数据结构
  • 建议:选择权重: FillType > 数据结构,因为“FillType”影响的是显示效果,而“数据结构”影响的是重建速度

关于Path绘制路径的常用方法总结

  • moveTo:移动起点;移动下一次操作的起点位置

  • setLastPoint:设置终点;重置当前path中最后一个点位置,如果在绘制之前调用,效果和moveTo相同

  • lineTo:连接直线;添加上一个点到当前点之间的直线到Path

  • close:闭合路径;连接第一个点连接到最后一个点,形成一个闭合区域

  • addRect, addRoundRect, addOval, addCircle, addPath, addArc, arcTo:添加内容;添加(矩形圆角矩形椭圆路径圆弧) 到当前Path (注意addArc和arcTo的区别)

  • isEmpty:是否为空;判断Path是否为空

  • isRect:是否为矩形;判断path是否是一个矩形

  • set:替换路径;用新的路径替换到当前路径所有内容

  • offset:偏移路径;对当前路径之前的操作进行偏移(不会影响之后的操作)

  • quadTo, cubicTo:贝塞尔曲线;分别为二次和三次贝塞尔曲线的方法

  • rMoveTo, rLineTo, rQuadTo, rCubicTorXxx方法不带r的方法是基于原点的坐标系(偏移量), rXxx方法是基于当前点坐标系(偏移量)

  • setFillType, getFillType, isInverseFillType, toggleInverseFillType:填充模式;设置填充模式、获取填充模式、判断和切换填充模式

  • incReserve查看案例:提示方法;提示Path还有多少个点等待加入(这个方法貌似会让Path优化存储结构)

  • op:布尔操作(API19);对两个Path进行布尔运算(区域分割交集处理)

  • computeBounds:计算边界;计算Path的边界

  • reset, rewind:重置路径;清除Path中的内容,reset不保留内部数据结构,但会保留FillType;rewind会保留内部的数据结构,但不保留FillType

  • transform:矩阵操作;矩阵变换坐标位置

posted @ 2024-07-25 22:06  阿俊学JAVA  阅读(80)  评论(0编辑  收藏  举报