SVG矢量动画
一、概述
相较于png、jpg等位图通过存储像素点来记录图像,svg (scalable vector graphics)拥有一套自己的语法,通过描述的形式来记录图形。android并不直接使用原始的svg格式图片,而是将其转化为vectordrawable。vectordrawable是一个xml格式的drawable,是矢量图在android中的原始资源,引用方法和png、jpg等其他drawable一样。但是,vectordrawable仍然只是一个静态图片,和png、jpg等没什么区别,如果想让vectordrawable “动”起来,就需要用到animatedvectordrawable。animatedvectordrawable才是真正的矢量动效图,其原理就是把vectordrawable和objectanimator结合起来,通过objectanimator属性动画控制vectordrawable,利用矢量图形的特性,从而达到各种炫酷的效果。
二、开发流程
我们下面以一个解锁动效为例,来讲解矢量图动效开发的整个流程。
(1)svg到vectordrawable
svg源文件由美工制作(sketch、ai等工具均可输出svg),vectordrawable也可以通过svg2android工具直接由svg转化而来。
网址:
工具:
本示例我们需要一个锁的矢量图ic_lock_32dp
<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="32dp" android:height="32dp" android:viewportheight="110" android:viewportwidth="110"> <group android:name="g_lock_arc" android:pivotx="38" android:scalex="1.0"> <path android:name="lock_arc" android:pathdata="m71.6,29v-5.4c0-8.9-7.6-16-17-16s-17,7.2-17,16v29" android:strokecolor="#000000" android:strokelinecap="round" android:strokelinejoin="round" android:strokewidth="4" /> </group> <path android:name="lock_rect" android:pathdata="m39,29h32c5.5,0,10,4.5,10,10v32c0,5.5-4.5,10-10,10h39c-5.5,0-10-4.5-10-10v39c29,33.5,33.5,29,39,29z" android:strokecolor="#000000" android:strokewidth="4" /> </vector>
矢量图主要是由path和group组成,矢量动画实际上就是通过控制path和group的属性变化来实现动画效果的。
(2)vectordrawable到animatedvectordrawable
有了矢量图,就可以设计矢量动画了。分析上面的解锁动效,整个解锁过程可拆分为两部分
- 上曲线(name="lock_arc")线条收缩
- 上曲线组(name="g_lock_arc")整体水平翻转
我们设计矢量动画图avd_lock_to_open如下:
<?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/ic_lock_32dp" ><!--引用的矢量图--> <!--曲线收缩--> <target android:name="lock_arc" android:animation="@anim/anim_lock_to_open_p1" /> <!--曲线组翻转--> <target android:name="g_lock_arc" android:animation="@anim/anim_lock_to_open_p2" /> </animated-vector>
分析上面代码
- android:drawable指定的是我们将对哪个矢量图做动效
- target标签的android:name指定要对矢量图的哪个部分做动效
- target标签的android:animation引用一个objectanimator,指定要对该部分做什么动画
anim_lock_to_open_p1通过控制trimpathstart属性对path(name="lock_arc")做了收缩:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <objectanimator android:duration="300" android:interpolator="@android:anim/accelerate_interpolator" android:propertyname="trimpathstart" android:startoffset="100" android:valuefrom="0" android:valueto="0.24" android:valuetype="floattype"/> </set>
anim_lock_to_open_p2通过控制scalex属性对group(name="g_lock_arc")做了水平翻转:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <objectanimator android:duration="400" android:interpolator="@android:anim/linear_interpolator" android:propertyname="scalex" android:valuefrom="1" android:valueto="-1" android:startoffset="450" android:valuetype="floattype"/> </set>
通过startoffset可以精确控制两个动画的执行顺序。至此,我们已经写好了animatedvectordrawable。
(3)animatedvectordrawable的使用
animatedvectordrawable本身还是一个drawable,和其他图片的引用方式是一样的。这里我们把它设置给imageview
<?xml version="1.0" encoding="utf-8"?> <framelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <imageview android:id="@+id/img" android:layout_gravity="center" android:layout_width="64dp" android:layout_height="64dp" android:src="@drawable/avd_lock_to_open"/> </framelayout>
矢量动态图不会自动播放,在需要播放的时候执行如下代码
drawable drawable = mimageview.getdrawable(); if (drawable instanceof animatable) { ((animatable) drawable).start(); }
至此,一个矢量动效的开发和引用已经全部完成了。但有时候我们不想每次都主动调用start方法来播放动效,而是希望根据控件的状态(如state_pressed)改变自动播放相应的过渡动效,这时就需要用到animated-selector了。
(4)animated-selector用法
在讲解animated-selector之前,我们先看一下selector的用法,它可以根据控件的不同状态显示不同的drawable,如
<?xml version="1.0" encoding="utf-8" ?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 默认时的背景图片 --> <item android:drawable="@drawable/pic1" /> <!-- 没有焦点时的背景图片 --> <item android:state_window_focused="false" android:drawable="@drawable/pic2" /> <!--获得焦点时的图片背景 --> <item android:state_focused="true" android:drawable="@drawable/pic3" /> <!--选中时的图片背景 --> <item android:state_selected="true" android:drawable="@drawable/pic4" /> </selector>
但使用selector时,状态切换是瞬间完成的,没有过渡。如果希望在状态切换时增加动效,那就需要用到animated-selector。为了讲解,我们仿照矢量动态图avd_lock_to_open再写一个闭锁的动态图avd_open_to_lock。其中会用到一个矢量图ic_open_32dp和两个objectanimator:anim_open_to_lock_p1、anim_open_to_lock_p2,代码类似,不再展示。我们直接看animated_selector的写法:
<?xml version="1.0" encoding="utf-8"?> <animated-selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/state_on" android:drawable="@drawable/ic_lock_32dp" android:state_checked="true" /> <item android:id="@+id/state_off" android:drawable="@drawable/ic_open_32dp" /> <transition android:drawable="@drawable/avd_lock_to_open" android:fromid="@id/state_on" android:toid="@id/state_off" /> <transition android:drawable="@drawable/avd_open_to_lock" android:fromid="@id/state_off" android:toid="@id/state_on" /> </animated-selector>
分析上面代码:
- item标签指定了确定状态下的drawable
- transition标签指定了状态切换时执行的矢量动效
通常情况下,我们会手动设置控件的状态,以便在适当的时候显示我们想要的动效。比如,我们希望点击imageview时在两个矢量动效之间切换:
boolean ischecked = false; mimageview.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { ischecked = !ischecked; inal int[] stateset = {ischecked ? android.r.attr.state_checked : 0}; mimageview.setimagestate(stateset, true);//设置state_checked状态 } });
使用animated-selector后,我们不再需要主动去调start( )方法来播放动效,只需要设置控件状态即可。
最终效果:
至此,我们已经完成了一套完整的矢量动效开发流程,目录结构如下:
源码:myapplication.zip
三、总结
我们已经知道svg动效实际上就是通过控制path或group的属性变化来达到动画效果,下面列出常用的可控属性
name | tags | comment |
---|---|---|
strokealpha | path | 定义路径边框的透明度 |
strokewidth | ||
strokecolor | ||