Android中的矢量图资源通常以VectorDrawable形式存在,而描述图形轮廓的核心就是<path>元素里的pathData属性。pathData使用一组紧凑的指令字符串来描绘路径,这些指令大多继承自SVG路径语法,最常用的是M、L、C、Z四个命令。掌握它们之后,开发者就能脱离对位图切片的依赖,仅用文本描述出任意缩放不变形的图标。

一、pathData与VectorDrawable的坐标规则
VectorDrawable的<vector>根节点会声明viewportWidth和viewportHeight,这相当于一个虚拟画布。pathData中的所有坐标都基于这个虚拟画布进行定位,与最终显示尺寸无关。例如viewportWidth为100、viewportHeight为100时,坐标(50,50)就表示画布中心点。这样做的好处是,同一份矢量图可以缩放到任意dp尺寸而不会产生锯齿。
pathData命令分为大写与小写两种形式。大写命令后面的坐标表示绝对坐标,小写命令则表示相对坐标,即相对于当前画笔位置的偏移量。理解这一点对后续阅读复杂路径非常重要,因为它决定了坐标是相对于画布原点还是相对于上一步终点。下面的示例定义了一个100x100画布,并绘制了一条从(10,10)到(90,90)的斜线。
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:strokeColor="#FF000000"
android:strokeWidth="4"
android:pathData="M10,10 L90,90" />
</vector>
在上面的XML中,<path>标签的android:pathData属性包含了M10,10和L90,90两条命令。命令与命令之间通常使用空格或者逗号分隔参数,但是否加空格并不会影响解析,例如M10,10L90,90也可以。早期Android版本对某些分隔格式支持不够稳定,因此在生成兼容性较好的资源时,建议在命令与数字参数之间保持清晰的分隔。
二、直线绘制指令M、L以及闭合指令Z
M是moveto的缩写,表示将画笔移动到某个坐标点,但不会画出线条。它通常作为路径的起始命令。L是lineto的缩写,表示从当前点绘制一条直线到指定坐标。Z是closepath的缩写,表示从当前点绘制一条直线回到当前子路径的起点,从而形成封闭图形。Z命令不需要任何参数。
例如要绘制一个矩形,可以先移动到左上角(20,20),然后用L命令依次连接另外三个点,最后使用Z闭合。pathData写作:M20,20 L80,20 L80,80 L20,80 Z。这段命令会生成一个完整的正方形轮廓,如果设置了fillColor,矩形内部会被填充;如果只设置strokeColor,则只显示边框。
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:fillColor="#FF4285F4"
android:pathData="M20,20 L80,20 L80,80 L20,80 Z" />
</vector>
上面的pathData中,M20,20是绝对移动,L80,20从(20,20)画横线到(80,20),L80,80再画竖线到(80,80),L20,80画横线到(20,80),Z从(20,80)画线回到起点(20,20)。这样四条边就围成了一个正方形。注意Z闭合后,路径的终点会重新变为起点,因此如果后面要继续添加新命令,需要注意当前点已经改变。
M与L还可以使用小写形式m和l来表达相对坐标。比如m20,20表示从原点偏20,20,但如果路径刚开始,小写m的行为等同于大写M,因为没有当前点。l10,0则表示从当前点向右偏移10个单位。相对坐标在翻译设计工具导出的SVG时很常见,因为工具会经常使用小写命令来简化连续绘制。
三、三次贝塞尔曲线指令C详解
C是curveto的缩写,用于绘制三次贝塞尔曲线。它的完整语法是:C 控制点1x,控制点1y 控制点2x,控制点2y 终点x,终点y。也就是说,一条C命令包含三组坐标参数。曲线从当前画笔位置出发,受两个控制点影响,最终到达终点。控制点的位置会决定曲线弯曲的方向和程度。
贝塞尔曲线的计算原理是:曲线在起点处朝着第一个控制点的方向前进,在终点处从第二个控制点的方向进入。控制点距离起点或终点越远,曲线向该方向拉伸得就越明显。三次贝塞尔曲线可以表达非常丰富的曲线形态,因此成为图标绘制中最常用的曲线命令。例如M20,80 C20,20 80,20 80,80这条命令会从(20,80)开始,先向(20,20)方向弯曲,再从(80,20)方向进入终点(80,80),形成一条类似字母S的平滑曲线。
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:fillColor="#FF34A853"
android:pathData="M20,80 C20,20 80,20 80,80 L80,80 Z" />
</vector>
上述代码中,C20,20 80,20 80,80的含义是:控制点1为(20,20),控制点2为(80,20),终点为(80,80)。起点在M20,80处。由于两个控制点都位于上方,曲线会向上鼓起。后面的L80,80实际是一条零长度线段,这里只是为了演示C后可以继续追加L和Z。真实图标中,通常会使用多个C命令连续绘制复杂轮廓,例如心形、水滴或者云朵。
如果需要接在C之后继续绘制平滑曲线,SVG还提供了S命令,它可以自动镜像前一个C的第二个控制点来生成更光滑的连接。不过在Android的VectorDrawable中,S命令并非始终被所有版本完整支持,因此为了保证兼容性,多数开发者仍然习惯显式写出多个C命令,或者使用H、V等命令辅助直线。重点还是先掌握C的三组参数和大小写相对坐标。
四、综合案例:用M、L、C、Z绘制一个对勾图标
下面用一个常见的对勾图标来展示M、L、C、Z组合使用。对勾主体由一条向下再向右上折回的折线构成,起点处可以用小段贝塞尔曲线让边缘更圆润。画布为100x100,路径从(20,55)开始,先向下偏右画一段短曲线到(40,75),再用L画到(55,60),最后画到(85,35)。
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:strokeColor="#FF0F9D58"
android:strokeWidth="6"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:pathData="M20,55 C20,55 30,75 40,75 L55,60 L85,35" />
</vector>
这段路径中,M20,55先把画笔移动到左侧,C20,55 30,75 40,75绘制了一小段起始处略微软化的曲线,让对勾的尾部不是生硬的直线,而带一点弧度。接着L55,60和L85,35完成两次折线转折。由于没有使用Z闭合,这个对勾是开放路径,适合用strokeColor来描边。同时android:strokeLineCap和android:strokeLineJoin可以控制线段端点和转折处的形状,round值会让图标看起来更柔和。
在调试复杂pathData时,建议把虚拟画布扩大到100或200,并用临时不同的填充色和描边色观察轮廓。设计工具导出的SVG中,路径往往会被自动优化成大量连续的小写c命令,代码会显得很长。但只要把c转换为C并重新分组控制点,就能逐步还原图形结构。关键还是要多练习拆分坐标,尤其是三次贝塞尔曲线的两个控制点,它们决定了整个图标的视觉风格。
综合来看,M负责定位,L负责直线,C负责曲线,Z负责闭合。这四个命令已经能够描述绝大多数Android矢量图标。把它们组合起来,再配合fillColor、strokeColor、strokeWidth等属性,就能在XML中完整表达一个可缩放的图形资源,而无需依赖额外的位图文件。
Android VectorDrawablepathDataSVG路径语法修改时间:2026-08-19 11:40:04