SVG和矢量动画

在Android5.0(API Level 21)中开始支持SVG的绘制,SVG有以下几个特点:

  • 可伸缩的矢量图形(Scalable Vector Graphics)
  • 使用XML格式定义图形
  • 图形在放大或改变尺寸时,图像不会失真
  • 基于W3C标准,与Web中的矢量图通用

Android中可以使用<path>标签绘制SVG,相比Bitmap,SVG最大的优点是缩放不会失真,这样不需要为不同的分辨率设计多套图标,从而缩小APK的体积。

path标签和SVG指令

使用<path>标签创建SVG,类似于用指令的方式控制一只画笔绘制图像。<path>标签支持的SVG指令有:

  • M = moveto(M X,Y):将画笔移动到指定的坐标位置,但未发生绘制
  • L = lineto(L X,Y):画直线到指定的坐标位置
  • H = horizontal lineto(H X):画水平线到指定的X坐标位置
  • V = vertical lineto(V Y):画垂直线到指定的Y坐标位置
  • C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三次贝塞尔曲线
  • S = smooth curveto(S X2,Y2,ENDX,ENDY):三次贝塞尔曲线,一般跟在C或S命令后面
  • Q = quadratic curveto(Q X,Y,ENDX,ENDY):二次贝塞尔曲线
  • T = smooth quadratic curveto(T ENDX,ENDY):二次贝塞尔曲线,一般跟在Q或T命令后面
  • A = elliptical arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧线
  • Z = closepath():关闭路径

使用以上指令时,需要注意:

  • 坐标轴为以(0,0)为中心,X轴水平向右,Y轴水平向下。
  • 所有指令大小写均可。大写绝对定位,参照全局坐标系;小写相对定位,参照父容器坐标系。
  • 指令和数据间的空格可以省略。
  • 同一指令连续出现多次可以只声明一次。

指令的详细解释可以参考MDN文档

SVG指令的写法固定,并且比较复杂,一般可以通过SVG编辑器来绘制SVG图像,然后将其转换为SVG代码。常用的SVG编辑器有在线的Method Draw和离线的Inkscape

SVG绘制

在Android中,可以使用VectorDrawable创建基于XML的SVG。在XML中创建SVG时,path标签是SVG的最小单位,而group标签将不同的path标签进行组合,最终形成一棵SVG树。

下面是一个“心形”矢量图的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="utf-8"?>
<!-- res/drawable/heart.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
<!-- SVG的实际大小 -->
android:width="256dp"
android:height="256dp"
<!-- SVG的划分比例,绘制path时使用的参数就是相对这两个值进行换算的 -->
android:viewportHeight="32"
android:viewportWidth="32">

<group
android:name="heart"
android:rotation="0">
<!-- draw a path -->
<path
android:fillColor="#8fff"
android:pathData="M20.5,9.5
c-1.955,0,-3.83,1.268,-4.5,3
c-0.67,-1.732,-2.547,-3,-4.5,-3
C8.957,9.5,7,11.432,7,14
c0,3.53,3.793,6.257,9,11.5
c5.207,-5.242,9,-7.97,9,-11.5
C25,11.432,23.043,9.5,20.5,9.5z"/>
</group>
</vector>

矢量动画

在Android中,可以使用AnimatedVectorDrawable实现矢量动画。AnimatedVectorDrawable的主要功能是将静态的VectorDrawable和动态的ObjectAnimator连接起来,产生动画效果。因此,矢量动画最终还是通过属性动画实现的。

使用矢量动画一般有以下几个步骤:

  • 在res/drawable/路径下利用vector标签定义要进行动画的矢量图
  • 在res/drawable/路径下利用animated-vector标签定义矢量动画
  • 在res/anim/路径下利用objectAnimator标签定义矢量动画使用的属性动画

矢量图中能够产生动画效果的元素是path和group元素,在定义矢量动画时,需要进行动画的元素必须要定义唯一的android:name属性。

矢量动画的具体示例如下:

定义矢量图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- res/drawable/vectordrawable.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="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>

根据name属性定义矢量动画:

1
2
3
4
5
6
7
8
9
10
<!-- res/drawable/animvectordrawable.xml -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vectordrawable" >
<target
android:name="rotationGroup"
android:animation="@anim/rotation"/>
<target
android:name="v"
android:animation="@anim/path_morph"/>
</animated-vector>

定义矢量动画对应的属性动画:

1
2
3
4
5
6
<!-- res/animator/rotation.xml -->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="6000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"/>
1
2
3
4
5
6
7
8
9
10
<!-- res/animator/path_morph.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="3000"
android:propertyName="pathData"
android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z"
android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0z"
android:valueType="pathType"/>
</set>

需要注意的是,对pathData属性进行动画时,valueTo和valueFrom值必须要兼容,即要有同样数量的命令,并且命令的参数数量要相同。

坚持原创技术分享,您的支持将鼓励我继续创作!