nonzero填充规则基于环绕数:从点引射线,顺时针边+1、逆时针边−1,和非零则填充;重叠反向路径导致镂空,自相交或复合形状易出意外,需用evenodd验证或工具查看路径方向。

nonZero 填充规则到底怎么算?
Android 的 android:fillType="nonZero" 是默认值,但它不是“画哪儿填哪儿”那么简单——它靠**环绕数(winding number)** 判断是否填充:从某点往任意方向画一条射线,每遇到一条**顺时针路径边**就 +1,遇到**逆时针路径边**就 −1;最终和不为 0,该点就被认为在“内部”,会被填充。
这意味着:如果你画了两个重叠的三角形,一个顺时针、一个逆时针,它们重叠区域的环绕数是 0,那块就会“透明”——哪怕你没写 evenOdd,也会出现镂空效果。
- 顺时针闭合路径(如
M0,0 L10,0 L5,10 Z)→ 贡献 +1 - 逆时针闭合路径(如
M0,0 L5,10 L10,0 Z)→ 贡献 −1 - 单个路径里混用顺/逆向子路径(比如先 M-L-L-Z 再 M-L-L-Z)→ 各自独立计算环绕数
-
nonZero对「自相交路径」(如五角星)填充结果可能反直觉,建议用evenOdd验证对比
pathData 方向怎么控制顺/逆时针?
Android 不检查 pathData 的几何朝向,只忠实地按你写的坐标顺序连线并闭合。所以“顺时针”完全取决于你写点的顺序——就像手动画多边形:从左上→右上→右下→左下→Z,大概率是顺时针;反过来就是逆时针。
实操中别靠肉眼猜,用工具辅助:
- 在 Android Studio 中预览 vector asset,开启「Show Path Direction」(小齿轮图标 → Show Path Direction),箭头会标出每段走向
- 用在线 SVG 编辑器(如 svgviewer.dev)粘贴
pathData,它会高亮显示填充区域,快速验证 - 简单测试法:画一个矩形
M0,0 H10 V10 H0 Z→ 顺时针;改成M0,0 V10 H10 V0 Z→ 逆时针(Y 增大向下)
fillType="nonZero" 在哪些场景容易翻车?
最常踩坑的是「复合形状」和「描边+填充混用」——nonZero 会把 stroke 和 fill 当作同一路径参与环绕数计算,但 stroke 本身不闭合,不会改变环绕数;真正影响的是 fill 区域的拓扑结构。
- 用
C或L拼出带内孔的形状(比如圆环),只写一个 path →nonZero很可能填满整个外轮廓,内孔不空(因为没定义“逆向内圈”) - 多个
<path></path>叠加时,各自独立计算环绕数,但渲染层会叠加 alpha,造成颜色变深或边缘发虚(尤其配合android:fillAlpha) - 从 SVG 导入时,某些工具导出的 path 默认用逆时针绘制 logo 文字,导入后文字被“挖空”,实际只是
nonZero把它判为外部 - targetSdkVersion
嵌套在 <path></path>内且设了fillType,可能直接 crash 或静默失效(错误提示类似android:endX attribute not found)
要不要主动写 android:fillType="nonZero"?
不用。它本来就是默认值,显式声明纯属冗余,还可能误导协作者以为“这里必须是非零填充”。真正该写的是 evenOdd——当你需要明确支持镂空、奇偶穿插、或兼容设计师给的 SVG 逻辑时。
另外注意:fillType 只作用于当前 <path></path>,不影响同级其他 path,也不继承 group;如果你有旋转/缩放 group,填充规则仍按原始 pathData 计算,不受 transform 影响。
复杂图标里路径方向难肉眼判断,又不想反复试错?老老实实拆成多个 <path></path>,每个单独配 fillType,比硬刚一个 giant pathData 更可靠。










