本篇文章主要是对SVG的一个介绍和使用,以及Android中对SVG的一个支持,从而可以帮助我们在android下很轻松的通过SVG实现一些非常酷炫的动画效果。
SVG 是使用 XML 来描述二维图形和绘图程序的语言。
它具备以下的特点:
- SVG 指可伸缩矢量图形 (Scalable Vector Graphics)
- SVG 用来定义用于网络的基于矢量的图形
- SVG 使用 XML 格式定义图形
- SVG 图像在放大或改变尺寸的情况下其图形质量不会有所损失
- SVG 是万维网联盟的标准
- SVG 与诸如 DOM 和 XSL 之类的 W3C 标准是一个整体
用户可以直接用代码来描绘图像,可以用任何文字处理工具打开SVG图像,通过改变部分代码来使图像具有交互功能,并可以随时插入到HTML中通过浏览器来观看。
在 2003 年一月,SVG 1.1 被确立为 W3C 标准。
参与定义 SVG 的组织有:太阳微系统、Adobe、苹果公司、IBM 以及柯达。
与其他图像格式相比,使用 SVG 的优势在于:
- SVG 可被非常多的工具读取和修改(比如记事本)
- SVG 与 JPEG 和 GIF 图像比起来,尺寸更小,且可压缩性更强。
- SVG 是可伸缩的
- SVG 图像可在任何的分辨率下被高质量地打印
- SVG 可在图像质量不下降的情况下被放大
- SVG 图像中的文本是可选的,同时也是可搜索的(很适合制作地图)
- SVG 可以与 Java 技术一起运行
- SVG 是开放的标准
- SVG 文件是纯粹的 XML
- SVG 的主要竞争者是 Flash。
与 Flash 相比,SVG 最大的优势是与其他标准(比如 XSL 和 DOM)相兼容。而 Flash 则是未开源的私有技术。
从上诉的描述中可以看到SVG是一个可伸缩的矢量图,而且相较于JPG和GIF图像尺寸都要更小,并且可以直接在xml中写入。正式由于这样的特点,那么 Android 从 5.0 提供了新的API VectorDrawable,通过该对象,我们可以使用矢量图SVG,在编写xml文件中,通过关键的几个标签节点vector,animated-vector,path完成对SVG的编写以及动画的实现。
可以看到 VectorDrawable 是继承与Drawable,并且官网说明了是用于创建一个drawable通过XML文件中申明根节点 vector 来实现一个android下的矢量图。
VectorDrawable 的出现也意味着以前我们放在mdpi, hdpi, xhdpi, xxhdpi中的部分图片资源(适合用矢量图描述的,比如图标)只用一个VectorDrawable 替代就可以了。
在vector节点下我们可以使用 “group、clip-path、path” 来描述一个drawable图形;“group”节点定义一组path或者子group,而path元素定义需要绘制的路径。clip-path节点定义当前绘制的剪切路径。注意,clip-path 只对当前的 group 和子 group 有效。
接下来大家可以通过以下的代码来学习VectorDrawable的定义:
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="100"
android:viewportWidth="100">
"path1"
android:pathData="
M 20,80
L 50,80 80,80"
android:strokeColor="@android:color/holo_green_dark"
android:strokeLineCap="round"
android:strokeWidth="5"/>
"path2"
android:pathData="
M 20,20
L 50,20 80,20"
android:strokeColor="@android:color/holo_green_dark"
android:strokeLineCap="round"
android:strokeWidth="5"/>
这段代码就是在Res下的drawable目录下建立了一个 xml 文件,然后指定根节点为 vector 写出的两根线段的效果。效果图如下:
上述代码中就是定义了一个 group 包含了两个path,而这两个 path 分别代表一根直线的绘制。这里要重点去理解的是 path 节点下的 pathData属性。
以下这些命令用于路径数据:
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):三次贝塞曲线
Q = quadratic Belzier curveto(Q X,Y,ENDX,ENDY):二次贝塞曲线
T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射前面路径后的终点
A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧线
Z = closepath():关闭路径
注释:以上所有命令均允许小写字母。大写表示绝对定位,小写表示相对定位。
对于做 Android 开发的我们来说要掌握这么多命令,并且如果需要绘制一个比较复杂的图形那么可能 pathData中需要写很多的数据,这肯定不是我们想看到的结果,比如下面的这个效果:
对应的SVG代码为:
"1.0" encoding="iso-8859-1"?>
对于这么庞大的数据计算我们肯定是不可能去手动画的,而这张图片也是我从 FLATICON 网站上随便找的一张 SVG 图片,down下来就是现成的 svg 文件了,我们可以直接使用到我们android 中来,所以以后开发中可以让 UI 给我们这样的 SVG 文件就行了,当然也有 SVG编辑工具直接产出一些好看的 SVG图片,都可以在 http://www.flaticon.com中找到。
AnimatedVectorDrawable 同样继承 Drawable,不过它实现了 Animatable 接口,专门用于让 VectorDrawable 动起来。
首先来看官方的说明:
This class animates properties of a VectorDrawable with animations defined using ObjectAnimator or AnimatorSet.
Starting from API 25, AnimatedVectorDrawable runs on RenderThread (as opposed to on UI thread for earlier APIs). This means animations in AnimatedVectorDrawable can remain smooth even when there is heavy workload on the UI thread. Note: If the UI thread is unresponsive, RenderThread may continue animating until the UI thread is capable of pushing another frame. Therefore, it is not possible to precisely coordinate a RenderThread-enabled AnimatedVectorDrawable with UI thread animations. Additionally, onAnimationEnd(Drawable) will be called the frame after the AnimatedVectorDrawable finishes on the RenderThread.
AnimatedVectorDrawable can be defined in either three separate XML files, or one XML.
AnimatedVectorDrawable 通过 ObjectAnimator 或 AnimatorSet 对 VectorDrawable 的某个属性作动画。
从API-25开始,AnimatedVectorDrawable 运行在 RenderThread (相反地,早期API是运行在UI线程)。这也就是说 AnimatedVectorDrawable 在UI线程繁忙时也能保持流畅运行。
如果UI线程没有反应,RenderThread 会持续动画计算,直到UI线程有能力推进下一帧。因此,没有办法准确地同步 RenderThread-enabled 的 AnimatedVectorDrawable 和 UI 线程中的 Animations。此外, Animatable2.AnimationCallback.onAnimationEnd(drawable) 肯定会在 RenderThread 渲染完 AnimatedVectorDrawable 最后一帧时被调用。
Animated vector drawable 可以让 group 和 path 元素的属性动态变化。group 定义一组 path 或者子 group,而 path 元素定义需要绘制的路径。当你想让 VectorDrawable 呈现动画效果,在定义 VectorDrawable 的时候需要为 group 和 path 的 android:name 属性设置一个唯一的名字,以便在Animated vector drawable中找到它们。比如在上面我们画的两根直线demo,我现在想让两根直线做出以下的动画效果:
在上面的两根直线 vector 代码中是由两段 path 定义出来的,也分别定义了 name 分别为 path1 path2,所以接下来只需要定义一个 AnimatedVectorDrawable 来分别执行这两段 path的动画。
代码如下:
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
android:drawable="@drawable/two_line">
"@animator/anim_path1"
android:name="path1"/>
"@animator/anim_path2"
android:name="path2"/>
注意上面:
一般我们使用 propertyName 节点都是传入基本动画(平移,缩放,旋转,透明度)
此处传入的为:pathData,效果为让动画沿着设定的 valueFrom 和 valueTo 的数据执行
最后要加入节点 android:valueType=”pathType”,表示数据类型为 path 类型,否则会报错
在 target 标签中需要分别为当前你要执行的 path 指定一个 属性动画,比如 anim_path1,代码如下:(必须是属性动画,所以在 animator 下创建)
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
android:duration="500"
android:propertyName="pathData"
android:interpolator="@android:interpolator/bounce"
android:valueFrom="
M 20,80
L 50,80 80,80"
android:valueTo="
M 20,80
L 50,50 80,80"
android:valueType="pathType"
>
anim_path2的代码也是类似:
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
android:duration="500"
android:propertyName="pathData"
android:interpolator="@android:interpolator/bounce"
android:valueFrom="
M 20,20
L 50,20 80,20"
android:valueTo="
M 20,20
L 50,50 80,20"
android:valueType="pathType"
>
这里还分别给两个动画指定了一个动画插值器:bounce,使合并的时候具有碰撞效果。
最后再来一个输入框的背景动画,效果如下:
结合svg动画和基本逻辑代码实现EditText的输入特效
在 activity 主界面中准备一个 ImageView 和两个 EditText ,第一个 EditText 没有背景(后续将为 ImageView 设置图片来使得 EditText 看起来像有一个背景一样),第二个 EditText 存在默认背景
这里直接贴出主要的 vector 和 animated_vector 代码,更详细的代码,请到GitHUb下载查看:https://github.com/zphuanlove/AnimationProject
首先是输入框背景,一根直线和一个对勾的代码:
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
android:width="300dp"
android:height="96dp"
android:viewportHeight="48"
android:viewportWidth="150">
"bottom"
android:pathData="
M 0,23
L 140,23"
android:strokeAlpha="0.8"
android:strokeColor="@color/colorAccent"
android:strokeLineCap="square"
android:strokeWidth="1"/>
"right"
android:pathData="
M 128,13
l 3,3
l 5,-6"
android:strokeWidth="2"
android:strokeColor="@color/colorAccent"
android:strokeLineCap="round"/>
接着就是控制对勾和直线的动画效果,第一种动画:直线显示出来,对勾隐藏:
"http://schemas.android.com/apk/res/android"
android:drawable="@drawable/et_bg">
"bottom"
android:animation="@animator/bottom_line_out" />
"right"
android:animation="@animator/right_default_gone" />
bottom_line_out动画代码:
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
android:duration="300"
android:propertyName="trimPathEnd"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
>
right_default_gone动画代码:
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
android:duration="1"
android:valueFrom="1"
android:valueTo="1"
android:propertyName="trimPathStart"
android:valueType="floatType">
第二种动画就是直线和对勾隐藏,第三种动画就是对勾显示,与第一种动画都是类似就不在此贴代码了。
通过本篇文章可以对SVG有一个比较基础的了解,以及如何在 Android下实现通过 SVG 实现我们的自定义动画效果!
Thanks!