Android动画机制与使用技巧(五)——Android 5.X SVG 矢量动画机制

Google在Android 5.X 中增加了对SVG 矢量图形的支持,这对于创建新的高效率动画具有非常重大的意义。那首先了解SVG的含义。

  • 可伸缩矢量图形(Scalable Vector Graphics)
  • 定义用于网络的基于矢量的图形
  • 使用XML格式定义图形
  • 图像在放大或改变尺寸的情况下其图形质量不会有所损失
  • 万维网联盟的标准
  • 与诸如DOM和XSL之类的W3C标准是一个整体

SVG在Web上的应用非常广泛,在Android 5.X之前的Android版本上,可以通过一些第三方开源库来在Android中使用SVG。而在Android 5.X之后,Android中添加了对SVG的path标签的支持。从而让开发者可以使用SVG来创建更加丰富的动画效果。那么SVG对比传统的Bitmap,究竟有什么好处呢?Bitmap(位图)通过在每个像素点上存储色彩信息来表达图像,而SVG是一个绘图标准。与Bitmap相比,SVG最大的优点就是放大不会失真。而且Bitmap需要为不同分辨率设计多套图标,而矢量图则不需要。

path标签

使用path标签创建SVG,就像用指令的方式来控制一只画笔,例如移动画笔到某一坐标位置,画一条线,画一条曲线,结束。path标签所支持的指令有以下几种。

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():关闭路径

在使用上面的指令时,需注意以下几点:

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

SVG常用指令

  • L

    绘制直线的指令是“L”,代表从当前点绘制直线到给定点。“L”之后的参数是一个点坐标,如“L 200 400”绘制直线。同时,还可以使用“H”和“V”指令来绘制水平、竖直线,后面的参数是x坐标(H指令)或y坐标(V指令)。

  • M

    M指令类似Android绘图中path类的moveTo方法,即代表将画笔移动到某一点,但并不发生绘制动作。

  • A

    A指令用来绘制一段弧线,且允许弧线不闭合。可以把A指令绘制的弧线想象成是椭圆的某一段,A指令以下有七个参数。

    1)RX,RY指所在椭圆的半轴大小

    2)XROTATION指椭圆的X轴与水平方向顺时针方向夹角,可以想象成一个水平的椭圆绕中心点顺时针旋转XROTATION的角度。

    3)FLAG1只有两个值,1表示大角度弧线,0为小角度弧线。

    4)FLAG2只有两个值,确定从起点至终点的方向,1为顺时针,0为逆时针。

    5)X,Y为终点坐标

SVG编辑器

SVG参数的写法固定且复杂,因此完全可以使用程序来实现,所以一般通过SVG编辑器来编辑SVG图形。通过可视化编辑好图形后,点击View Source就可以转换为SVG代码。SVG在线编辑器网址

Android中使用SVG

Google在Android 5.X 中提供了下面两个新的API来帮助支持SVG。

  • VectorDrawable
  • AnimatedVectorDrawable

其中,VectorDrawable让你可以创建基于XML的SVG图形,并结合AnimatedVectorDrawable来实现动画效果。

VectorDrawable

在XML中常见一个静态的SVG图形,通常会形成下图所示的树形结构。

path是SVG树形结构中的最小单位,而通过Group可以将不同的path进行组合。

如何在drawable目录下创建SVG图形,首先需要在XML中通过vector标签来声明对SVG的使用,代码如下(vector.xml):

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="200dp"
    android:viewportWidth="100"
    android:viewportHeight="100">

    <group android:name="test"
        android:rotation="0">
        <path android:fillColor="@android:color/holo_blue_light"
            android:pathData="M 25 50
                              a 25,25 0 1,0 50,0"/>
    </group>

</vector>

width与height属性表示该SVG图形的具体大小,而viewportWidth与viewportHeight属性表示SVG图形划分的比例。后面在绘制path时所示使用的参数,就是根据这两个值来进行转换的,比如上面的代码,将200dp划分为100份,如果在绘制图形时使用坐标(50, 50),则意味着该坐标位于该SVG图形正中间。因此,width、height的比例与viewportWidth、viewportHeight的比例,必须保持一致,不然图形就会发生压缩、变形。

通过添加group标签和path标签来绘制一个SVG图形,其中pathData就是绘制SVG图形所用到的指令。在上面的代码中,先使用“M”指令,将画笔移动到(25, 50)这个坐标,再通过A指令来绘制一个圆弧并填充,效果图如下。

由于这里使用了android:fillColor属性来绘制图形,因此绘制出来的是一个填充的图形,如果要绘制一个非填充的图形,可以使用以下属性。

            android:strokeColor="@android:color/holo_blue_light"
            android:strokeWidth="2"

绘制效果如下所示:

AnimatedVectorDrawable

AnimatedVectorDrawable的作用就是给VectorDrawable提供动画效果。Google工程师将AnimatedVectorDrawable比喻为一个胶水,通过AnimatedVectorDrawable来连接静态的VectorDrawable和动态的objectAnimator。

如何使用AnimatedVectorDrawable实现SVG图形的动画效果。首先,在XML文件中通过animated-vector标签来声明对AnimatedVectorDrawable的使用,并制定其作用的path或group,代码如下所示。

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vector">

    <target
        android:animation="@anim/anim_path1"
        android:name="test"/>

</animated-vector>

对应的vector即为静态的VectorDrawable。

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="200dp"
    android:viewportHeight="100"
    android:viewportWidth="100">

    <group
        android:name="test"
        android:rotation="0">

        <path
            android:pathData="M 25 50
                              a 25,25 0 1,0 50,0"
            android:strokeColor="@android:color/holo_blue_light"
            android:strokeWidth="2"/>
    </group>

</vector>

AnimatedVectorDrawable中指定的target的name属性必须与VectorDrawable中需要作用的name属性保持一致,这样系统才能找到要实现动画的元素。通过AnimatedVectorDrawable中target的animation属性,将一个动画作用到了对应name的元素上,objectAnimator代码如下:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="4000"
    android:propertyName="rotation"
    android:valueFrom="0"
    android:valueTo="360" />

可以看到,对动画效果的实现,还是通过属性动画来实现的,只是属性稍有不同。

在group标签和path标签中添加了rotation、strokeColor、pathData等属性,那么在objectAnimator中,就可以通过指定android:propertyName=”XXX”属性来选择控制哪一种属性,通过android:valueFrom=”XXX”和android:valueTo=”XXX”属性,可以控制动画的起始值,唯一需要注意的是,如果指定属性为pathData,那么需要添加一个属性——android:valueType=”pathType”来告诉系统进行pathData变换。类似的情况,可以使用rotation进行旋转动画、使用strokeColor实现颜色变化,使用pathData进行形状、位置变化。

当所有的XML文件准备好以后,就可以在代码中控制SVG动画,可以非常方便地将一个AnimatedVectorDrawable XML文件设置给一个ImageView作为背景显示。

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:src="@drawable/anim_vector" />

在程序中,只需要使用以下代码,即可开始SVG动画。

        Drawable drawable = imageView.getDrawable();
        if(drawable instanceof Animatable){
            ((Animatable) drawable).start();
        }

SVG动画实例

线图动画

在Android 5.X 中Google大量引入了线图动画。当页面发生改变时,页面上的icon不再是生硬地切换,而是通过非常生动的动画效果,转换为另一种形态。如下图所示,点击图像时,开始SVG动画,上下两根线会从中间折断并向中间折起,最终形成一个“X”。

要实现这样一个效果,首先创建一个静态的SVG图形,即静态的VectorDrawable,并绘制为如图所示的初始状态。

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="200dp"
        android:height="200dp"
        android:viewportHeight="100"
        android:viewportWidth="100">

    <group>
        <path
            android:name="path1"
            android:pathData="
            M 20,80
            L 50,80 80,80"
            android:strokeColor="@android:color/holo_green_dark"
            android:strokeLineCap="round"
            android:strokeWidth="5"/>

        <path
            android:name="path2"
            android:pathData="
            M 20,20
            L 50,20 80,20"
            android:strokeColor="@android:color/holo_green_dark"
            android:strokeLineCap="round"
            android:strokeWidth="5"/>
    </group>

</vector>

path1和path2分别绘制了两条直线,每条直线由三个点控制,即起始点、中间点和终点,形成初始状态。接下来实现变换的objectAnimator动画。

<?xml version="1.0" encoding="utf-8"?>
<!--anim_path1.xml-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:duration="500"
                android:interpolator="@android:anim/bounce_interpolator"
                android:propertyName="pathData"
                android:valueFrom="
            M 20,80
            L 50,80 80,80"
                android:valueTo="
            M 20,80
            L 50,50 80,80"
                android:valueType="pathType"/>
<!--代码中定义了一个pathType的属性动画,并指定了变化的起始值。
在SVG的路径变化属性动画中,变化前后的节点数必须相同,这也是为什么前面需要使用三个点来绘制一条直线的原因,
因为后面需要中间点进行动画交换。-->
<?xml version="1.0" encoding="utf-8"?>
<!--anim_path2.xml-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:duration="500"
                android:interpolator="@android:anim/bounce_interpolator"
                android:propertyName="pathData"
                android:valueFrom="
            M 20,20
            L 50,20 80,20"
                android:valueTo="
            M 20,20
            L 50,50 80,20"
                android:valueType="pathType"/>

最后使用AnimatedVectorDrawable来将VectorDrawable与objectAnimator黏合在一起。

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
                 android:drawable="@drawable/trick">

    <target android:name="path1" android:animation="@anim/anim_path1"/>
    <target android:name="path2" android:animation="@anim/anim_path2"/>

</animated-vector>

模拟三球仪

三球仪是天文学中一个星象仪器,用来模拟地、月、日三个星体的绕行轨迹,即地球绕太阳旋转,月球绕地球旋转的同时,绕太阳旋转,如下图所示:

首先,同样是需要绘制一个静态的地、月、日三星系统。

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="200dp"
        android:height="200dp"
        android:viewportHeight="100"
        android:viewportWidth="100">

    <group
        android:name="sun"
        android:pivotX="60"
        android:pivotY="50"
        android:rotation="0">

        <path
            android:name="path_sun"
            android:fillColor="@android:color/holo_blue_light"
            android:pathData="
            M 50,50
            a 10,10 0 1,0 20,0
            a 10,10 0 1,0 -20,0"/>

        <group
            android:name="earth"
            android:pivotX="75"
            android:pivotY="50"
            android:rotation="0">

            <path
                android:name="path_earth"
                android:fillColor="@android:color/holo_orange_dark"
                android:pathData="
                M 70,50
                a 5,5 0 1,0 10,0
                a 5,5 0 1,0 -10,0"/>

            <group>

                <path
                    android:fillColor="@android:color/holo_green_dark"
                    android:pathData="
                    M 90,50
                    m -5,0
                    a 4,4 0 1,0 8,0
                    a 4,4 0 1,0 -8,0"/>

            </group>
        </group>
    </group>
</vector>

上述代码中,在“sun”这个group中,有一个“earth”group,同时使用android:pivotX和android:pivotY属性来设置其旋转中心。这个VectorDrawable分别代表了太阳系系统和地月系系统。下面对这两个group分别进行SVG动画:

<?xml version="1.0" encoding="utf-8"?>
<!--sun动画-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:duration="4000"
                android:propertyName="rotation"
                android:valueFrom="0"
                android:valueTo="360"/>
<?xml version="1.0" encoding="utf-8"?>
<!--earth动画-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:duration="4000"
                android:propertyName="rotation"
                android:valueFrom="0"
                android:valueTo="360"/>

这里为了简化示例,让两个动画效果的实现完全相同。最后,最后使用AnimatedVectorDrawable黏合SVG静态图形和动画。

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
                 android:drawable="@drawable/earth_moon_system">

    <target android:name="sun" android:animation="@anim/anim_sun"/>
    <target android:name="earth" android:animation="@anim/anim_earth"/>

</animated-vector>

轨迹动画

Android对SVG的支持给我们带来了很多好玩的特效,例如可以将propertyName指定为trimPathStart,这个属性用来控制一个SVG Path的显示比例,例如一个圆形的SVG,使用trimPathStart动画,可以像画出一个圆一样来绘制一个圆,从而形成一个轨迹动画,下面这个实例就展示了绘制轨迹的动画效果,用来绘制搜索框中的一个放大镜,效果如下所示:

代码如下:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="160dp"
        android:height="30dp"
        android:viewportHeight="30"
        android:viewportWidth="160">

        <path
            android:name="search"
            android:pathData="
            M 141,17
            A 9 9 0 1 1 142 16
            L 149 23"
            android:strokeColor="#ff3570be"
            android:strokeLineCap="round"
            android:strokeWidth="2"
            android:strokeAlpha="0.8"/>

        <path
            android:name="bar"
            android:pathData="
            M 0,23
            L 149 23"
            android:strokeColor="#ff3570be"
            android:strokeLineCap="square"
            android:strokeWidth="2"
            android:strokeAlpha="0.8"/>

</vector>
<?xml version="1.0" encoding="utf-8"?>

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@android:interpolator/accelerate_decelerate"
    android:propertyName="trimPathStart"
    android:valueFrom="0"
    android:valueTo="1"
    android:valueType="floatType" />
<!--trimPathStart就是利用0到1的百分比来按照轨迹绘制SVG图像。类似的,还有trimPathEnd这个属性。-->
<?xml version="1.0" encoding="utf-8"?>

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/searchbar">

    <target
        android:name="search"
        android:animation="@anim/anim_searchbar" />

</animated-vector>

代码地址

时间: 2024-08-03 12:39:44

Android动画机制与使用技巧(五)——Android 5.X SVG 矢量动画机制的相关文章

Android开发笔记(一百三十二)矢量图形与矢量动画

矢量图形VectorDrawable 与水波图形RippleDrawable一样,矢量图形VectorDrawable也是Android5.0之后新增的图形类.矢量图不同于一般的图形,它是由一系列几何曲线构成的图像,这些曲线以数学上定义的坐标点连接而成.具体到实现上,则需开发者提供一个xml格式的矢量图形定义,然后系统根据矢量定义自动计算该图形的绘制区域.因为绘图结果是动态计算得到,所以不管缩放到多少比例,矢量图形都会一样的清晰,不像位图那样拉大后会变模糊. 矢量图形的xml定义有点复杂,其结构

Android群英传笔记——第七章:Android动画机制和使用技巧

Android群英传笔记--第七章:Android动画机制和使用技巧 想来,最近忙的不可开交,都把看书给冷落了,还有好几本没有看完呢,速度得加快了 今天看了第七章,Android动画效果一直是人家中十分重要的一部分,从早期的Android版本中,由于动画机制和绘图机制的不健全,Android的人机交互备受诟病,Android从4.X开始,特别是5.X,动画越来越完善了,Google也开始重视这一方面了,我们本章学习的主要内容有 Android视图动画' Android属性动画 Android动画

Android群英传知识点回顾——第七章:Android动画机制与使用技巧

7.1 Android View动画框架 7.1.1 透明度动画 7.1.2 旋转动画 7.1.3 位移动画 7.1.4 缩放动画 7.1.5 动画集合 7.2 Android属性动画分析 7.2.1 ObjectAnimator 7.2.2 PropertyValuesHolder 7.2.3 ValueAnimator 7.2.4 动画事件的监听 7.2.5 AnimatorSet 7.2.6 在XML中使用属性动画 7.2.7 View的animate方法 7.3 Android布局动画

第三章 Android绘图机制与处理技巧

1.屏幕尺寸信息 屏幕大小:屏幕对角线长度,单位“寸”:分辨率:手机屏幕像素点个数,例如720x1280分辨率:PPI(Pixels Per Inch):即DPI(Dots Per Inch),它是对角线的像素点数除以屏幕大小得到的:系统屏幕密度:android系统定义了几个标准的DPI值作为手机的固定DPI.(注,最后一个有笔误,正确的是1080x1920)独立像素密度(DP):android系统使用mdpi屏幕作为标准,在这个屏幕上1dp=1px,其他屏幕可以通过比例进行换算.在hdpi中,

Android动画总结系列(6)——矢量图形与矢量动画

按照我一开始的打算,上面一篇文章应该是"Android动画总结系列(5)--属性动画源码分析",不过属性动画源码分析写起来还比较复杂,因为某些原因,我把精力投入到矢量动画这块了,第5篇估计会在后面一两周写完.本篇文章,我写的是Android5.0引入的新动画效果--矢量动画,初步打算后面还会加一篇源码分析. 一.概述 1.1 简述 Android应用的不断发展带来了安装包过大的尴尬,而Android之前一直都不支持矢量图形,是引起尴尬的一个重要原因.其实Android绘制界面时也是通过

超酷创意分段式SVG文字动画特效

这是一款基于segment.js制作的非常有创意的分段式SVG文字动画特效.这个文字动画特效通过动画SVG的描边路径来制作各种文字的动画效果,效果非常的赞. 这个SVG文字动画特效的第一个DEMO中的最后几个例子使用了mo.js插件,一款由Oleg Solomka编写的用于制作网页图形动画的JavaScript库插件.通过mo.js,可以制作出效果更为震撼的文字动画效果. 在线预览   源码下载 特效中使用的字体是exquisite lowercase font,一套极富创意的WEB字体. 使用

Android笔记(六十五) android中的动画——属性动画(propertyanimation)

补间动画只能定义起始和结束两个帧在“透明度”.“旋转”.“倾斜”.“位移”4个方面的变化,逐帧动画也只能是播放多个图片,无法满足我们日常复杂的动画需求,所以谷歌在3.0开始,推出了属性动画(property animation) 属性动画已经不再是针对View来设计的了,也不仅限定于只能实现移动.缩放.淡入淡出这几种动画操作,同时也不再是一种视觉上的动画效果了.它实际上是一种不断的对值进行操作的机制,并将值赋值到指定对象的指定属性上,可以是任意对象的任意属性. ValueAnimator Val

【朝花夕拾】Android自定义View篇之(五)Android事件分发及传递机制

前言 在自定义View中,经常需要处理Android事件分发的问题,尤其在有多个输入设备(如遥控.鼠标.游戏手柄等)时,事件处理问题尤为突出.Android事件分发机制,一直以来都是一个让众多开发者困扰的难点,至少笔者在工作的前几年中,没有特意研究它之前,就经常云里雾里.实际上,该问题的“七寸”就是dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev).onTouchEvent(MotionEvent ev

Android绘图机制与处理技巧

一屏幕的尺寸信息 1屏幕参数 2系统屏幕密度 3独立像素密度dp 4单位换算 二2D绘图基础 三Android XML 绘图 Bitmap Shape Layer Selector 四绘图技巧 Canvas 一.屏幕的尺寸信息 1屏幕参数 屏幕大小 指屏幕对角线的长度,通常使用"寸"来度量,例如4.7寸手机 5.5寸手机等. 分辨率 分辨率是指手机屏幕的像素点个数,例如720*1280是指屏幕分变率,指宽有720个像素点,高有1280个像素点. PPI 每英寸像素(Pixels Per