自定义View(Path二)

上一篇内容自定义View(Path一)
http://www.jianshu.com/p/7c1fde3e6c13

上一篇把Path常用的方法大致讲了一下,这里再稍微做一些补充。

Path.setFillType()

我们知道,Paint(画笔)可以设置填充模式mPaint.setStyle(Paint.Style.FILL);那么问题来了,如果我们绘制两个相交的矩形:

自定义View(Path二)_第1张图片

应该是填充成这样呢?

自定义View(Path二)_第2张图片

还是这样呢?

自定义View(Path二)_第3张图片

android给我们提供了设置填充模式的方法:

Path.setFillType(Path.FillType ft) //设置填充方式

我们要给一个图形内部填充颜色,首先需要分清哪一部分是外部,哪一部分是内部,机器不像我们人那么聪明,机器是如何判断内外呢?
我们点进setFillType()方法的源码,发现参数(Path.FillType)是一个枚举类型,共有4个选择,方法中填入不同的 FillType 值,就会有不同的填充效果。FillType 的取值如下:

EVEN_ODD
WINDING (默认值)
INVERSE_EVEN_ODD
INVERSE_WINDING
其中后面的两个带有 INVERSE_ 前缀的,只是前两个的反色版本,所以只要把前两个,即 EVEN_ODD 和 WINDING,搞明白就可以了。

EVEN_ODD 和 WINDING 的原理

EVEN_ODD
即 even-odd rule (奇偶原则):对于平面中的任意一点,向任意方向射出一条射线,这条射线和图形相交的次数(相交才算,相切不算哦)如果是奇数,则这个点被认为在图形内部,是要被涂色的区域;如果是偶数,则这个点被认为在图形外部,是不被涂色的区域。

射线的方向无所谓,同一个点射向任何方向的射线,结果都是一样的,不信你可以试试。从上面的描述可以看出,射线每穿过图形中的一条线,内外状态就发生一次切换,这就是为什么 EVEN_ODD 是一个「交叉填充」的模式。
WINDING
即 non-zero winding rule (非零环绕数原则):首先,它需要你图形中的所有线条都是有绘制方向的(上一篇讲过,Path所有的路径都是由绘制方向的):

自定义View(Path二)_第4张图片
图片来源于网络

然后,同样是从平面中的点向任意方向射出一条射线,但计算规则不一样:以 0 为初始值,对于射线和图形的所有交点,遇到每个顺时针的交点(图形从射线的左边向右穿过)把结果加 1,遇到每个逆时针的交点(图形从射线的右边向左穿过)把结果减 1,最终把所有的交点都算上,得到的结果如果不是 0,则认为这个点在图形内部,是要被涂色的区域;如果是 0,则认为这个点在图形外部,是不被涂色的区域。
自定义View(Path二)_第5张图片

和 EVEN_ODD 相同,射线的方向并不影响结果。

Android其他与填充模式相关的方法

这些都是Path中的方法。

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

重置路径

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

|方法| 是否保留FillType设置| 是否保留原有数据结构|
| ------------- |:-------------:|
|reset |是 |否|
|rewind| 否 |是|
这个两个方法应该何时选择呢?

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

布尔操作(API19)

布尔操作与我们中学所学的集合操作非常像,只要知道集合操作中的交集,并集,差集等操作,那么理解布尔操作也是很容易的。

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

布尔操作有两个方法:

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

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

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

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

逻辑名称 类比 说明 示意图
DIFFERENCE 差集 Path1中减去Path2后剩下的部分
自定义View(Path二)_第6张图片
REVERSE_DIFFERENCE 差集 Path2中减去Path1后剩下的部分
自定义View(Path二)_第7张图片
INTERSECT 交集 Path1与Path2相交的部分
自定义View(Path二)_第8张图片
UNION 并集 包含全部Path1和Path2
自定义View(Path二)_第9张图片
XOR 异或 包含Path1与Path2但不包括两者相交的部分
自定义View(Path二)_第10张图片

最后我们尝试用布尔操作绘制一个太极logo:

 protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(getWidth()/2,getHeight()/2);

        Path path1,path2,path3,path4,path5,path6;
        path1 = new Path();
        path2 = new Path();
        path3 = new Path();
        path4 = new Path();
        path5 = new Path();
        path6 = new Path();

        path2.addRect(-200,-200,0,200, Path.Direction.CW);
        path3.addCircle(0, -100, 100, Path.Direction.CW);
        path4.addCircle(0, 100, 100, Path.Direction.CCW);
        path5.addCircle(0,-100,10, Path.Direction.CCW);
        path6.addCircle(0,100,10, Path.Direction.CCW);

        path1.addCircle(0,0,200, Path.Direction.CW);
        path1.op(path2, Path.Op.INTERSECT);
        path1.op(path3, Path.Op.UNION);
        path1.op(path4, Path.Op.DIFFERENCE);
        path1.op(path5, Path.Op.DIFFERENCE);
        path1.op(path6, Path.Op.UNION);

        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawPath(path1,mPaint);
        mPaint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(0,0,200,mPaint);
    }
自定义View(Path二)_第11张图片

Path基本讲完

你可能感兴趣的:(自定义View(Path二))