前言
SVG在前端中使用的一种语法规范,Vector在Android中使用而且只支持一种Path标签,主要是为了提高SVG的解析效率,减少SVG加载时候效率问题。相对于普通的静态jpg、png等图片来说,SVG能够实现缩放不失真,而且减小了drawable体积;但是SVG相对于静态图片需要更多的处理时间。通常SVG都是由设计提供,如果想自己编辑SVG图片可以使用在线SVG编辑器 http://editor.method.ac/。
SVG语法
Vector常用的命令:
命令 | 释义 | 使用方式 |
---|---|---|
M | MoveTo | M x,y |
L | LineTo | L x,y |
Z | closePath | Z |
H | Horizontal LineTo | H x,y |
V | Vertical LineTo | V x,y |
A | 绘制弧线 | A rx,ry,xrotation,flag1,flag2,x,y |
以上就是一些基本的SVG命令,其中A的参数需要解释一下:
rx、ry:椭圆的半轴大小;xrotation:椭圆X轴与水平方向顺时针方向的夹角大小;flag1:只有两个值,1代表取大角度弧线,0代表取小角度弧线;flag2:只有两个值,确定绘制的方向,1代表顺时针,0代表逆时针;X、Y:终点坐标值。
有几点要注意:坐标轴为以(0,0)为中心,X轴水平向右,Y轴水平向下。所有指令大小写均可。大写绝对定位,参照全局坐标系;小写相对定位,参照父容器坐标系。指令和数据间的空格可以省略。同一指令出现多次可以只用一个。
在Android Studio的New->Vector Asset里可以直接添加一个SVG XML文件。
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
</vector>
上面的android:width/android:height代表SVG真正展示的大小,android:viewportWidth/android:viewportHeight则代表将当前的SVG分成多少份,后面的M19,9这样的坐标值就是根据这个划分来确定位置。如果后面把width/height增加缩小那么SVG绘制出来的图像也不会失真。
Android中的SVG目前只支持path标签,fillColor代表填充色,pathData就是绘制SVG图像的指令序列。M19,9代表moveTo 19,9,前面把SVG图像分割成了24*24格,19,9就是将画笔移动到第19行第9列的位置,h-4也就是画横向直线。其实pathData就是指令+参数这样的基本元素持续不断的出现的数据,只有z没有参数,这表示完成了一条路径的绘制。根据上面的图像结果可以看出这个SVG绘制了两条路径,因而有两个z指令。
兼容性
目前只兼容minSDK >= 21的版本,最早几乎没有兼容性。AppCompat23.2静态Vector支持2.1+,动态Vector支持Android3.0+,几乎可以兼容大部分使用场景,现在的AppCompat基本上都能够支持SVG。要使用SVG动画效果,需要配置build.gradle文件,设置高度23的AppCompat依赖支持,如果还是有问题需要在使用的地方增加AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)设置。
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
compile 'com.android.support:appcompat-v7:23+'
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
除了以上的适配方案外,还需要注意一下两点:
1. Path Morphing路径变换动画无法在低于L版本上使用
1. Path Interpolation在低于L版本系统不能使用自定义插值器
VectorDrawable使用
VectorDrawable能够很好的使用动画,如果想要在低于5.0版本上使用动画效果一定要使用ImageView/ImageButton的app:srcCompat属性来设置,直接使用android:src在低于5.0版本无法正确展示。不过笔者目前都是在6.0版本上测试的新动画效果,这里所有的演示都是用android:src属性来做。
下载图标
在网上下载文件的时候经常会看到动态的下载图标表明点击的对象是可下载的,这里就简单的使用前面添加的下载图标实现这个动画效果。需要注意的是通常下载图标的向下箭头和底部表示磁盘缓存的横线移动的方式不同,为了能够分别对两个路径做偏移动画,可以在pathData的第一个z处将两者分开。
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<group
android:name="download">
<path
android:fillColor="#FF000000"
android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7z"/>
</group>
<group
android:name="diskCache">
<path
android:fillColor="#FF000000"
android:pathData="M5,18v2h14v-2H5z"/>
</group>
</vector>
path对象本身并没有translateY/translateX属性,但是group对象却有这个属性,实际上这个动画效果需要放到group上执行。现在开始定义竖向的属性动画和横向的属性动画。
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="-1"
android:valueTo="1"
android:propertyName="translateX"
android:valueType="floatType"
android:repeatMode="reverse"
android:repeatCount="infinite"
android:duration="500">
</objectAnimator>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="-2"
android:valueTo="2"
android:propertyName="translateY"
android:valueType="floatType"
android:repeatMode="reverse"
android:repeatCount="infinite"
android:duration="500">
</objectAnimator>
最后需要使用vector-drawable将两者粘合起来。
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
<!-- 需要执行动画的svg图片 -->
android:drawable="@drawable/ic_file_download">
<!--执行动画的目标和动画,对向下箭头执行竖直方向偏移动画-->
<target
android:animation="@animator/vertical_translate"
android:name="download">
</target>
<!--对底部横线执行水平偏移动画-->
<target
android:animation="@animator/horizontal_translate"
android:name="diskCache" />
</animated-vector>
trimPathStart/trimPathEnd属性
trimPathStart属性表示截掉 从起点到某个位置的部分,保留剩下的部分;trimPathEnd 属性表示截掉从某个位置到终点的部分,保留剩下的部分。
属性名 | 属性值 | 效果 |
---|---|---|
trimPathStart | valueFrom:0,valueTo:1 | 线条从起点缩短到终点,即初始截断部分是0%,从起点开始逐渐扩大到终点,达到100% |
trimPathStart | valueFrom:1,valueTo:0 | 线条从终点增长到起点,即初始截断部分是100%,从终点开始逐渐缩小到起点,达到0% |
trimPathEnd | valueFrom:0,valueTo:1 | 线条从起点增长到终点,即初始截断部分是100%,从起点开始逐渐缩小到终点,达到0% |
trimPathEnd | valueFrom:1,valueTo:0 | 线条从终点缩短到起点,即初始截断部分是0%,从终点开始逐渐扩大到起点,达到100% |
这里用两个SVG图形来演示效果,一个是自定义的正方形,一个是Vector Asset里找到的放大镜。然后定义相关的trimPathStart和trimPathEnd动画,在animated-vector里将它们结合起来。
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:propertyName="trimPathStart"
android:repeatMode="restart"
android:repeatCount="infinite"
android:valueType="floatType"
android:valueFrom="0"
android:valueTo="1">
</objectAnimator>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:strokeColor="#FF000000"
android:strokeWidth="1"
android:name="path"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14zM12,10h-2v2L9,12v-2L7,10L7,9h2L9,7h1v2h2v1z"/>
</vector>
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_zoom_in">
<target
android:animation="@animator/trim_path_start"
android:name="path" />
</animated-vector>
图形变换动画
图形变换动画会将一个图形改变成另外一个图形的样子,比如说把一个三角性改变成一个正方形,或者反过来。这种动画效果其实就是利用SVG的pathData属性做动画,需要注意的是在定义路径时一定要保持两者SVG命令的对称性,也就是说两者的指令数目必须相同保证变换是兼容的。首先来定一个正方形的SVG图片,然后针对它的pathData做属性动画。
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:name="path"
android:strokeWidth="1"
android:strokeColor="@color/colorAccent"
android:pathData="M 2,2 L 22,2 L 22,22 L 2,22 Z"/>
</vector>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueType="pathType"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:propertyName="pathData"
android:valueFrom="M 2,2 L 22,2 L 22,22 L 2,22 Z"
android:valueTo="M 12,2 L 22,22 L 2,22 L 12,2 Z"
android:duration="2000">
</objectAnimator>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_rectangle">
<target
android:animation="@animator/path"
android:name="path" />
</animated-vector>
其中最主要的就是objectAnimator的propertyName=”pathData”和valueFrom里面是生成正方形的指令,而valueFrom里放的就是生成三角形的指令。
除了上面的正方形变三角形,网上还有两条横线变成叉号的图形变换动画,其实这种效果也是把直线变成两条45度角的两条线段相连接。上面的直线和下面的直线都做这样的变换,最终看起来两条横线就变成了一个叉号。
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:name="path"
android:strokeWidth="1"
android:strokeColor="@color/colorAccent"
android:pathData="M 2,2 L 12,2 22,2 M 2,22 L 12,22 22,22"/>
</vector>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:duration="2000"
android:propertyName="pathData"
android:valueType="pathType"
android:valueFrom="M 2,2 L 12,2 22,2 M 2,22 L 12,22 22,22"
android:valueTo="M 2,2 L 12,12 22,2 M 2,22 L 12,12 22,22">
</objectAnimator>
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_two_lines">
<target
android:animation="@animator/cross"
android:name="path">
</target>
</animated-vector>
图形变换的功能虽然十分强大,但是在低版本上暂时还无法支持,只有高于21的版本才能完全支持,实际使用中需要判断版本支持才能使用这种动画效果。查看本Demo源码请点击查看源码。