Android SVG 矢量动画机制

来源:互联网 发布:季羡林的孩子知乎 编辑:程序博客网 时间:2024/06/06 00:55

      Google 在 Android 5.X 中增加了对 SVG 矢量图形的支持。可从如下方面简单了解什么是 SVG。

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

      在 Android 5.X 之后,Android 中添加了对 SVG 的 <path> 标签的支持。从而让开发者可以使用 SVG 来创建更加丰富的动画效果。与 Bitmap 相比,SVG 最大的优点就是放大不会失真,Bitmap 需要为不同的分辨率设计多套图标,而矢量图则不会。

     下面来看看这两个效果图:




       这就是使用 SVG 做出来的效果。在这之前,先了解一下 <path> 标签。

       使用path标签时,就像用指令的方式来控制一只画笔,例如:移动画笔到某一坐标位置,画一条线,画一条曲线,结束。

     

     1、支持的指令:

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

     

    2、使用原则:

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

     注意,'M'处理时,只是移动了画笔, 没有画任何东西。 它也可以在后面给出上同时绘制不连续线。


    3、详细指令分析


        3.1、L H V指令


       绘制直线的指令是“L”,从当前点划线到给定点。 “L”之后的参数是一个点坐标,如“L 200 400”。 如果画水平线或垂直线,可以使用“H”和“V”指令,后面的参数是x(H指令)或y坐标(V指令)。


       M 起点X,起点Y L(直线)终点X,终点Y H(水平线)终点X V(垂直线)终点Y


      如:M 10,20 L 80,50 M 10,20 V 50 M 10,20 H 90




 

3.2、A指令



    允许不闭合。可以想像成是椭圆的某一段,共七个参数:

     A RX,RY,XROTATION,FLAG1,FLAG2,X,Y

   (1)RX,RY指所在椭圆的半轴大小。
   (2) XROTATION指椭圆的X轴与水平方向顺时针方向夹角,可以想像成一个水平的椭圆绕中心点顺时针旋转XROTATION的角度。
   (3) FLAG1只有两个值,1表示大角度弧线,0为小角度弧线。
   (4)FLAG2只有两个值,确定从起点至终点的方向,1为顺时针,0为逆时针。
   (5)X,Y为终点坐标。


     如:m 200,250 a 150,30 0 1 0 0,70




      注:以上的基本介绍来至于网络。



      SVG 编辑器

      SVG 参数的写法固定而且复杂,因此完全可以使用程序来实现,所以一般通过 SVG 编辑器来编辑 SVG 图形。可以在http://editor.method.ac/ 这个网址中下载离线的 SVG 编辑器,如:Inkscape,通过可视化编辑好图形后,点击 ViewSource 就可以转换为 SVG 代码了。


       怎样使用 SVG

       主要通过如下两个 API 来支持:

      (1)VectorDrawable  创建基于 XML 的 SVG 图形。

      (2)AnimatedVectorDrawable  实现动画效果。


         VectorDrawable  

         path 是 SVG 树形结构中的最小单位,而通过 Group 可以将可以将不同的 path 进行组合。而 VectorDrawable 的根标签为 <vector> ,所以在 drawable 目录下新建一个 xml ,最好是在 drawable-v21 目录下新建 xml 。和前面的博客《RippleDrawable 的简单使用》http://blog.csdn.net/antimage08/article/details/50390546 中类似。


         第一个效果现实

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:viewportHeight="100"    android:viewportWidth="100">    <group>        <path            android:name="path1"            android:strokeColor="@android:color/holo_red_dark"            android:strokeWidth="5"            android:strokeLineCap="round"            android:pathData="                M 20, 80                L 50, 80 80, 80"/>        <path            android:name="path2"            android:strokeColor="@android:color/holo_red_dark"            android:strokeWidth="5"            android:strokeLineCap="round"            android:pathData="                M 20, 20                L 50, 20 80, 20"/>    </group></vector>

        其中:两组宽高属性:height、width 和 viewportHeight、viewportWidth 分别有不同的含义。height、width 表示该 SVG 图形的具体大小,而 viewportHeight、viewportWidth 表示 SVG 图形划分的比例。上面代码将 200dp 划分为 100 份,如果在绘制图形时使用坐标 (50, 50),则意味着该坐标位于该 SVG 图形的正中间。height、width 的比例与  viewportHeight、viewportWidth 的比例必须保持一致,不然会发生压缩、变形。代码中的 pathData 就是绘制 SVG 图形所用到的指令。


        AnimatedVectorDrawable

            AnimatedVectorDrawable 就像胶水一样,通过 AnimatedVectorDrawable 来连接静态的 VectorDrawable  和动态地 objectAnimator。

       res/anim 目录下创建动画的 xml :

      objanimator1.xml :

<?xml version="1.0" encoding="utf-8"?><objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"    android:duration="500"    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"    android:interpolator="@android:anim/bounce_interpolator"></objectAnimator>

       objanimator2.xml :

<?xml version="1.0" encoding="utf-8"?><objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"    android:duration="500"    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"    android:interpolator="@android:anim/bounce_interpolator"></objectAnimator>

        AnimatedVectorDrawable 的 xml (同样是在 drawable-v21 目录下)

vetordrawable.xml :

<?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/objanimator1"        android:name="path1"/>    <target        android:animation="@anim/objanimator2"        android:name="path2"/></animated-vector>

      在上面的 3 个 xml 中,AnimatedVectorDrawable 中指定的 target 的 name 属性,必须与 VectorDrawable  中需要作用的 name 属性保持一致,系统才能找到要实现的动画元素。

       objectAnimator 中指定了 pathData ,那么需要添加一个属性—— android:valueType="pathType" 来告诉系统进行 pathData 变换。

     

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

      activity_main.xml :

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.crazy.svgtest.MainActivity">    <ImageView        android:id="@+id/image"        android:layout_centerHorizontal="true"        android:src="@drawable/vetordrawable"        android:layout_width="wrap_content"        android:layout_height="wrap_content" /></RelativeLayout>


      MainActivity.java :

package com.crazy.svgtest;import android.graphics.drawable.Animatable;import android.graphics.drawable.Drawable;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.ImageView;public class MainActivity extends AppCompatActivity {    private ImageView imageView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        imageView = (ImageView)findViewById(R.id.image);        imageView.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                animateStart();            }        });    }    private void animateStart(){        Drawable drawable = imageView.getDrawable();        if (drawable instanceof Animatable)            ((Animatable)drawable).start();    }}

其中有动画开始的代码。



       第二个效果代码:


       drawable-v21 目录下:

       earth_moon_system.xml :

<?xml version="1.0" encoding="utf-8"?><vector xmlns:android="http://schemas.android.com/apk/res/android"    android:width="300dp"    android:height="300dp"    android:viewportHeight="150"    android:viewportWidth="150">    <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="90"            android:pivotY="50"            android:rotation="0">            <path                android:name="path_earth"                android:fillColor="@android:color/holo_orange_dark"                android:pathData="                    M 85,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 105,50                        m -5 0                        a 4,4 0 1,0 8 0                        a 4,4 0 1,0 -8,0" />            </group>        </group>    </group></vector>

其中 android:pivotX="" 和 android:pivotY="" 属性为设置其旋转中心。


       res/anim 目录下:

       anim_earth.xml :

<?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" />

      anim_sun.xml :

<?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" />


      drawable-v21 目录下:

      sun_system.xml :

<?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>


        activity_main.xml :

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context=".MainActivity">    <ImageView        android:id="@+id/image"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:src="@drawable/sun_system" /></RelativeLayout>

      MainActivity.java 如前面的 MainAcitivty.java 的代码。

0 0