在Android的VectorDrawable中,pathData是用一串字母和数字来描述矢量路径的微型语言。其中M、L、Z是最基础也最常用的三个指令,它们分别承担移动、画直线和闭合路径的职责。只要搞清楚这三个指令的坐标含义与执行顺序,就能用手写方式画出绝大多数基础几何图形。

一、M指令:移动画笔起点
M指令的全称是moveto,它的作用是将当前画笔位置移动到指定的坐标点,且不会在移动过程中绘制任何线条。在pathData里,M后面通常跟一对x、y坐标,例如M10,10表示把画笔移到坐标系的(10,10)位置。这个坐标以VectorDrawable自身的viewport坐标系为准,左上角为原点,向右为x正方向,向下为y正方向。
需要注意,M只影响起点,不代表画线。如果后面不接L或别的绘制指令,单独写M是不会显示任何图形的。另外,在一个pathData字符串中,第一次出现的M定义整条路径的起始点;如果在中间再次使用M,则代表开启一段新的子路径,之前的点不会和新的点自动连接。下面是一段仅包含M指令的代码,运行后界面上不会有可见内容:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:fillColor="#000000"
android:pathData="M10,10 M50,50" />
</vector>
上面的例子中虽然写了两个M,但因为没有L或Z参与,系统不知道要连接或填充什么区域,所以最终渲染为空。这也说明M只是定位,不是绘制。
二、L指令:绘制直线段
L指令即lineto,它从当前画笔所在点向给定的目标坐标画一条直连线段。例如当前点在M10,10之后位于(10,10),接着写L90,10,就会画出一条从(10,10)到(90,10)的水平线。L每次只处理一个终点,连续写多个坐标时可以省略重复的L,比如L90,10 90,90等价于L90,10 L90,90。
小写l表示相对坐标,即相对于当前点偏移量来画线;大写L是绝对坐标。初学者建议先用大写L减少换算错误。下面示例用M加两次L画出一条折线:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:strokeColor="#FF0000"
android:strokeWidth="2"
android:pathData="M10,10 L90,10 L90,90" />
</vector>
这段代码会渲染出两条相接的红色线段,构成一个直角。因为没有Z指令,末端不会自动连回起点,所以图形是开口的。如果改用fillColor而不是strokeColor,由于路径未闭合,填充区域会按隐含直线连接首尾来计算,这往往和直觉不同,因此是否需要闭合要用Z明确控制。
三、Z指令:闭合路径
Z指令即closepath,它不接收坐标参数,作用是从当前点画一条直线回到当前子路径的起始M点,并结束该子路径。Z常写在L序列之后,用来封口。比如M10,10 L90,10 L90,90 Z会画出两条边后再补一条回到(10,10)的斜边,形成一个直角三角形并自动闭合。
闭合对填充尤为重要:当使用fillColor时,未闭合路径系统也会自动视为闭合来填充,但显式写Z能确保描边也出现闭合边。以下示例展示带Z的三角形:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:fillColor="#0000FF"
android:pathData="M10,10 L90,10 L50,90 Z" />
</vector>
这里M定位到顶部左点,两个L画出上边和右侧边,Z自动连回起点形成底边,最终填充出实心三角形。如果去掉Z,填充区域虽仍在但描边会缺少底边,视觉上就不完整。
四、M、L、Z组合实践
把三个指令组合起来,可以描述任意由直线构成的图形。例如画一个空心正方形,用M定起点,L画三边,Z闭合:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:strokeColor="#009900"
android:strokeWidth="2"
android:pathData="M20,20 L80,20 L80,80 L20,80 Z" />
</vector>
该示例中四个点依次定义,Z补回第一条边位置,得到一个标准方框。若要在同一个path里画两个分离图形,可再写一个M开启新子路径,如M20,20 L80,20 L80,80 L20,80 Z M30,30 L70,30 L70,70 L30,70 Z,后者会在内部再描一个方框,两者互不连线。
掌握M、L、Z后,你会发现Android vector图形并不神秘,本质就是用坐标指令告诉系统从哪里起笔、连到哪里、是否回环。后续再接触C、Q等曲线指令时,也能沿用这一套路径思维轻松扩展。