如果你想让你的矢量图动起来, 你就需要理解pathData中的各个命令的含义.

此篇是 Understanding VectorDrawable pathData commands in Android 的译文, 如有疑问请查看原文.

打开任意一个VectorDrawable的XML文件, 你会发现pathData属性有一堆令人难以理解的命令.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="64dp"
android:width="64dp"
android:viewportHeight="600"
android:viewportWidth="600" >

<group
android:name="rotationGroup"
android:pivotX="300.0"
android:pivotY="300.0"
android:rotation="45.0" >

<path
android:name="v"
android:fillColor="#000000"
android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />

</group>
</vector>

它们通常是和path一起工作, path能解释这些命令并生成它们所代表的图形, 除此之外, 对于我们而言这些命令是有点神秘的.

VectorDrawable 是什么

VectorDrawable是矢量图的一种XML表示方式, 不像Bitmap/JPEG/GIF和PNG这些常见的图片那样随着放大或缩小而变得模糊.基于矢量图的这种优点就使得我们没有必要打包不同分辨率的图片, 所以它能够使APK的体积更小. 实际上, VectorDrawable像Canvas一样包含了一些path命令(描述如何画线条与圆弧). VectorDrawable的绘制与渲染是需要消耗时间与内存的, 这也就是为什么VectorDrawable最适合用来画那些简单的扁平的图形.

为什么需要理解这些命令呢? 我可以直接从SVG中得到它们啊!

就像最近流行的那样如果你想使你的VectorDrawable也具有动画效果, 其中一个条件就是VectorDrawable的起始动画与结束动画必须对应相同数量的path命令. 同时这也能够使你清楚这些命令是如何让图形有序地移动/变换以产生动画的. 为了这个目的, 理解这些path命令都是什么含义对我们是很有帮助的.

理解PathData命令

让我首先声明一下, 无论你有多努力去学习path命令, 你能理解的都是有限的, 除非你是一个天才.对我们而言这些矢量图命令可读性很低. 后来矢量文件像svg可以通过Android Studio VectorDrawable generator直接生成VectorDrawable, 这使得情况变的好了一些, 然而, 我发现有很多你可能需要的命令还是很复杂的.比如你要理解画圆与画圆弧的命令就要求你有很强的想像力或高超的数学运算能力.

命令基础

基础的path命令都是由字母后跟一个或多个数字组成. 数字通常都有,隔开, 但这不是必须. 看下面的例子:

1
2
3
4
5
M100,100 L300,100 L200,300 z
//or
M 100 100 L 300 100 L 200 300 z
//or
M1100,100L300,100L200,300z

字母可以是大写也可以是小写. 大写代表绝对位置, 小写代表相对位置.

常用命令

M or m (X,Y)+

移动到: 移动绘制点(Cursor)到指定位置, 大写代表绝对位置, 小写代表相对位置, 此命令后面跟地是X, Y坐标. 后面可能跟不止一对坐标, 如果跟多对则隐式的代表在这些点之间画线.

Z or z

闭合路径: 在路径的开始点与结束点之间画一条线. 后面不需要跟任何参数.

L or l (X,Y)+

连线: 在当前点与点(x, y)之间画线.

大写代表绝对坐标, 小写代表相对坐标. 此命令后可跟多对坐标点. 如果你指定了不只一对坐标点, 那就会画出多条线.

H or h (X)+

水平画线: 在当前点与横坐标X之间画一条水平线. 如果有多个X, 那就会画多条线. Y坐标保持不变. 大写H代表绝对坐标, 小写h代表相对坐标.

译者注: 其实这里如果有多个X, 在视觉上看只是一条线, 因为这些线会重合起来.

V or v (Y)+

垂直画线: 在当前点与纵坐标Y之间画一条竖直线. 如果有多个Y, 那就会画多条线. X坐标保持不变. 大写V代表绝对坐标, 小写v代表相对坐标.

示例

记着上面这些命令, 让我们解释一下上面我们说过的这些命令:

1
M100,100 L300,100 L200,300 z

M100,100: 移动光标到绝对坐标点 X=100, Y=100

L300,100: 画一条线到坐标点X=300 Y=100(起始点为(100,100))

L200,300: 画一条线到坐标点X=200 Y=300(起始点为(300,100))

z: 闭合路径, 从当前点画一条直线到路径起始点(100,100). 闭合路径后当前图形会被你设置的填充色填充. 如果你的图形不需要闭合路径, 你就可以不管它, 就像复选标记或一个十字图形.

如果我们把它画出来, 你会看到这个图形就是一个倒三角!

如果我们把它放到一个简单的VectorDrawable XML文件里, 我们就能看到结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
<vector xmlns:android="http://schemas.android.com/apk/res/android"
<!-- intrinsic size of the drawable -->
android:width="400px"
android:height="400px"
<!-- size of the virtual canvas -->
android:viewportWidth="400.0"
android:viewportHeight="400.0">
<path
android:fillColor="#0000FF"
android:strokeColor="#FFFFFF"
android:strokeWidth="4"
android:pathData="M100,100 L300,100 L200,300 z"/>
</vector>

知道这些你能做什么?

知道这么多, 你就可以做很多. 仅仅是了解了上面这些知识, 我就创建了一个有动画效果的Drawable.

画复选标记的最终命令如下:

1
M6,11 l3.5,4 l8,-7

我这里的画布大小是 24dp 24dp, 所以我将初始位置设置在了(6, 11), 这个点就是复选标记图形的起始点, 相对于起始点(6,11)我将坐标向下移动了(3.5, 4)然后又相对的移动了(8, -7)最终完成了复选标记的绘制. 因为我不想让我的复选标记最开始就显示, 所以最开始我将所有的*lineto命令参数设置都为(0,0), 然后我在第一幅动画中将第一条线从相对位置(0,0)相对移动了(3.5,4). 注意, 如果我使用绝对坐标的话, 这些坐标值都是不合理的.

1
2
3
Starting point:   M6,11 l0,0   l0,0
Animation Step 1: M6,11 l3.5,4 l0,0
Animation Step 2: M6,11 l3.5,4 l8,-7 //complete!

drawable/check_mark.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">

<group android:name="background">
<path
android:name="circle"
android:fillColor="@color/colorPrimary"
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0" />

</group>
<group android:name="check">
<path
android:name="tick"
android:pathData="M6,11 l0,0 l0,0"
android:strokeColor="@color/colorAccent"
android:strokeWidth="1" />

</group>

</vector>

drawable-v21/animated_check.xml

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/check_mark">

<target
android:name="tick"
android:animation="@anim/check_animation" />

</animated-vector>

anim/check_animation.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:ordering="sequentially"
android:shareInterpolator="false">

<!-- Step 1 -->
<objectAnimator
android:duration="@android:integer/config_shortAnimTime"
android:propertyName="pathData"
android:valueFrom="M6,11 l0,0 l0,0"
android:valueTo="M6,11 l3.5,4 l0,0"
android:valueType="pathType" />

<!-- Step 2 -->
<objectAnimator
android:duration="@android:integer/config_shortAnimTime"
android:propertyName="pathData"
android:valueFrom="M6,11 l3.5,4 l0,0"
android:valueTo="M6,11 l3.5,4 l8,-7"
android:valueType="pathType" />

</set>

Usage

1
2
3
4
5
6
7
8
9
<ImageView
android:id="@+id/imageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:visibility="visible"
app:srcCompat="@drawable/animated_tick" />


mImgCheck = (ImageView) findViewById(R.id.imageView);
((Animatable) mImgCheck.getDrawable()).start();

更多命令

下一步就是学习如何画圆弧了, 然而这就变得复杂了所以我打算引用W3.orgs上的Path文档

如果你想手动去画圆或圆弧我推荐你去读一下并理解它. 坦白说这些我并不擅长. 我的目的是让你明白理解这些命令是如何能帮你手动创建简单的矢量图并且使它们动起来的.

最后

如果你想构建更好的安卓应用, 那就多读读我的其它文章吧

哇! 你竟然读完了! 我们真应该一起出来玩玩! 请随意在Medium, LinkedIn, Google+ or Twitter.上粉我吧.

版权声明

文章版权归本人所有,如需转载需在明显位置处保留作者信息及原文链接 !