美文网首页
Android自定义View-Path完结篇

Android自定义View-Path完结篇

作者: 小的橘子 | 来源:发表于2019-08-28 00:01 被阅读0次

    Path方法介绍

    rXxx方法

    此类方法可以看到和前面的一些方法看起来很像,只是在前面多了一个r,那么它们有什么区别呢?
    rXxx方法的坐标使用的是相对位置(基于当前点的位移),而之前方法的坐标是绝对位置(基于当前坐标系的坐标)。

    举个栗子:

    Path path = new Path();
    
    path.moveTo(100,100);
    path.lineTo(100,200);
    
    canvas.drawPath(path,mDeafultPaint);
    

    将上述示例中lineTo改为rLineTo,再运行效果如下:

    在使用rLineTo之前,当前点的位置在 (100,100) , 使用了 rLineTo(100,200) 之后,下一个点的位置是在当前点的基础上加上偏移量得到的,即 (100+100, 100+200) 这个位置,故最终结果如上所示。

    PS: 此处仅以 rLineTo 为例,只要理解 “绝对坐标” 和 “相对坐标” 的区别,其他方法类比即可。

    填充模式

    Paint可以设置填充,这里的填充模式在Paint设置为填充时,会对图形渲染效果影响。

    我们要给一个图形内部填充颜色,首先需要分清图形的外部和内部,机器不像我们人那么聪明,机器是如何判断内外呢?
    机器判断图形内外,一般有以下两种方法:

    方法 判定条件 解释
    奇偶规则 奇数表示在图形内,偶数表示在图形外 从任意位置p作一条射线, 若与该射线相交的图形边的数目为奇数,则该p点是图形内部点,否则是外部点
    非零环绕数规则 环绕数为0表示在图形外,非零表示在图形内 首先使图形的边变为矢量(CW,CCW即表示了方向)。从任意位置p作一条射线,当从p点沿射线方向移动时,对在每个方向上穿过射线的边计数,每当图形的边从右到左穿过射线时,环绕数加1,从左到右时,环绕数减1。处理完图形的所有相关边之后,若环绕数为非零,则p为内部点,否则,p是外部点。

    接下来我们先了解一下两种判断方法是如何工作的。
    奇偶规则(Even-Odd Rule)
    这一个比较简单,也容易理解,直接用一个简单示例来说明。


    在上图中有一个四边形,我们选取了三个点来判断这些点是否在图形内部。
    • P1: 从P1发出一条射线,发现图形与该射线相交边数为0,偶数,故P1点在图形外部。
    • P2: 从P2发出一条射线,发现图形与该射线相交边数为1,奇数,故P2点在图形内部。
    • P3: 从P3发出一条射线,发现图形与该射线相交边数为2,偶数,故P3点在图形外部。

    非零环绕数规则(Non-Zero Winding Number Rule)
    假设绘制图形时为CW,也就是顺时针,如图:

    • P1: 从P1点发出一条射线,沿射线方向移动,并没有与边相交点部分,环绕数为0,故P1在图形外边。
    • P2: 从P2点发出一条射线,沿射线方向移动,与图形点左侧边相交,该边从左到右穿过穿过射线,环绕数-1,最终环绕数为-1,故P2在图形内部。
    • P3: 从P3点发出一条射线,沿射线方向移动,在第一个交点处,底边从右到左穿过射线,环绕数+1,在第二个交点处,右侧边从左到右穿过射线,环绕数-1,最终环绕数为0,故P3在图形外部。

    通常,这两种方法的判断结果是相同的,但也存在两种方法判断结果不同的情况,如下面这种情况:



    填充模式共有四种,是封装在Path中的一个枚举。

    模式 简介
    EVEN_ODD 奇偶规则
    INVERSE_EVEN_ODD 反奇偶规则
    WINDING 非零环绕数规则
    INVERSE_WINDING 反非零环绕数规则

    对于奇偶规则和非零环绕数规则都存在对立的INVERSE_XXX模式,这些模式判断结果和不带INVERSE的正好相反。例如对于一个矩形而言,使用奇偶规则会填充矩形内部,而使用反奇偶规则会填充矩形外部。

    Android与填充模式相关的方法

    方法 作用
    setFillType 设置填充规则
    getFillType 获取当前填充规则
    isInverseFillType 判断是否是反向(INVERSE)规则
    toggleInverseFillType 切换填充规则(即原有规则与反向规则之间相互切换)

    布尔操作

    布尔操作是两个Path之间的运算,主要作用是用一些简单的图形通过一些规则合成一些相对比较复杂,或难以直接得到的图形。

    如太极中的阴阳鱼,如果用贝塞尔曲线制作的话,可能需要六段贝塞尔曲线才行,而在这里我们可以用四个Path通过布尔运算得到,而且会相对来说更容易理解一点。



    代码如下:

    canvas.translate(mViewWidth / 2, mViewHeight / 2);
    
    Path path1 = new Path();
    Path path2 = new Path();
    Path path3 = new Path();
    Path path4 = new Path();
    
    path1.addCircle(0, 0, 200, Path.Direction.CW);
    path2.addRect(0, -200, 200, 200, Path.Direction.CW);
    path3.addCircle(0, -100, 100, Path.Direction.CW);
    path4.addCircle(0, 100, 100, Path.Direction.CCW);
    
    
    path1.op(path2, Path.Op.DIFFERENCE);
    path1.op(path3, Path.Op.UNION);
    path1.op(path4, Path.Op.DIFFERENCE);
    
    canvas.drawPath(path1, mDeafultPaint);
    

    Canvas中clipRect、clipPath 剪切方法理解一文中讲过Region.Op参数的作用,对于Path.Op参数和其非常类似。

    Path的布尔运算有五种逻辑,如下:

    逻辑名称 类比 说明 示意图
    DIFFERENCE 差集 Path1中减去Path2后剩下的部分
    REVERSE_DIFFERENCE 差集 Path2中减去Path1后剩下的部分
    INTERSECT 交集 Path1与Path2相交的部分
    UNION 并集 包含全部Path1和Path2
    XOR 异或 包含Path1与Path2但不包括两者相交的部分

    Path中的op方法预览

    boolean op (Path path, Path.Op op)
    boolean op (Path path1, Path path2, Path.Op op)
    

    两个方法中的返回值用于判断布尔运算是否成功,它们使用方法如下:

    // 对 path1 和 path2 执行布尔运算,运算方式由第二个参数指定,运算结果存入到path1中。
    path1.op(path2, Path.Op.DIFFERENCE);
    
    // 对 path1 和 path2 执行布尔运算,运算方式由第三个参数指定,运算结果存入到path3中。
    path3.op(path1, path2, Path.Op.DIFFERENCE)
    

    计算边界

    方法预览:

    void computeBounds (RectF bounds, boolean exact)
    

    这个方法主要作用是计算Path的边界区域,并将返回结果写入bounds矩形参数中

    • 对于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);   // 绘制边界
    

    重置路径

    重置Path有两个方法,分别是reset和rewind,两者区别主要有一下两点:

    方法 是否保留FillType设置 是否保留原有数据结构
    reset
    rewind

    这个两个方法应该何时选择呢?

    选择权重: FillType > 数据结构

    因为“FillType”影响的是显示效果,而“数据结构”影响的是重建速度。

    相关文章

      网友评论

          本文标题:Android自定义View-Path完结篇

          本文链接:https://www.haomeiwen.com/subject/atzyectx.html