Android vector path fillType nonZero XML填充规则详解

6次阅读

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

Android vector path fillType nonZero XML填充规则详解

nonZero 填充规则到底怎么算?

androidandroid: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 区域的拓扑结构。

  • CL 拼出带内孔的形状(比如圆环),只写一个 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 更可靠。

text=ZqhQzanResources