美文网首页Android UIAndroid开发Android进阶之路
Android中矢量图形的那些事 - SVG or Vector

Android中矢量图形的那些事 - SVG or Vector

作者: GinO_卓泓 | 来源:发表于2017-03-22 16:49 被阅读598次

    之前对矢量图形有所耳闻,但因为Android对于矢量图形的原生支持较晚,所以一直没好好研究过(16年2月25就出来的东西,其实就是懒 =。=)。最近工作上正好遇到一个产品需求,想用SVG来解决,借此机会对SVG及Android对于矢量图形的支持做了次了解,当然最后依然被SVG坑到,变成手写XML来解决需求,不过这都是题外话了。

    SVG是什么?

    Scalable Vector Graphics(可缩放矢量图形)是基于XML,用于描述二维矢量图形的图形格式。SVG由W3C制定,是一个开放标准。SVG本身允许包含3种图形对象类型:

    1. 矢量图形,包括矩形、圆、椭圆、多边形、直线、任意曲线等。
    2. 嵌入外部图像,包括png、jpeg、svg等。
    3. 文本。
      [from wikipedia]

    以下是一个简单的SVG格式示例:

    <?xml version="1.0" standalone="no"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
      <path d="M150 0 L75 200 L225 200 Z" />
    </svg>
    

    SVG是通过标签的形式来描述图形,比如<rect>矩形、<circle>圆形、<polygon>多边形等等普通标签,以及支持复杂的路径的标签<path>.其中Path标签可以通俗的理解为是用命令的方式来控制画笔,比如:
    ** 将画笔移动到指定坐标位置 -> 画一条直线到指定坐标位置 -> 再画一条曲线 -> 完成后抬起画笔结束**

    当然Path标签也需要对应的指令来完成操作,比如:
    M150 0 L75 200 L225 200 Z

    下面是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 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():关闭路径
    注意:以上所有命令均允许小写字母。大写表示绝对定位,小写表示相对定位。
    

    当然,以上只是SVG中很小的一部分,还有其他很多强大的功能。SVG本身已经被广泛使用,SVG矢量图与位图的差别,在于矢量图可以尽可能的放大而不失真,并且同一张图像,SVG的实现可能比png小很多。如果在精简安装包的时候,可以考虑部分图标改成矢量图的方式。

    Android中的矢量图替身Vector

    Android中对于矢量图形的处理,并没有选择直接支持SVG文件,所以云端返回的svg格式图片是无法直接被显示的。
    不过在Android 5.0中提出了矢量图片的支持,叫做Vector。通过创建<vector>标签的xml元素来完成对矢量图形的定义。

    <!-- res/drawable/heart.xml -->
    <vector xmlns:android="http://schemas.android.com/apk/res/android"
        <!-- intrinsic size of the drawable -->
        android:height="256dp"
        android:width="256dp"
        <!-- size of the virtual canvas -->
        android:viewportWidth="32"
        android:viewportHeight="32">
    
      <!-- draw a path -->
      <path android:fillColor="#8fff"
          android:pathData="M20.5,9.5
                            c-1.955,0,-3.83,1.268,-4.5,3
                            c-0.67,-1.732,-2.547,-3,-4.5,-3
                            C8.957,9.5,7,11.432,7,14
                            c0,3.53,3.793,6.257,9,11.5
                            c5.207,-5.242,9,-7.97,9,-11.5
                            C25,11.432,23.043,9.5,20.5,9.5z" />
    </vector>
    

    Android Studio从1.4版本开始,包含了一个Vector Asset Studio的工具,可以选择导入SVG或者PSD文件来帮助我们生成对应的vector xml文件。
    工具本身支持大多数主体元素,但并不包含所有的SVG或PSD特性。SVG导入后的vector xml格式主体参考的是SVG中的pathdata语法,但虚线等属性、科学计数法、图案填充(pattern)、渐变(gradient)等特定标签是不支持的。当然Vector Asset Studio在我们导入的时候会有明确的提示哪些是被支持的、哪些是不被支持的。

    以下两种方法都可以打开Vector Asset Studio:
    1. 对应的drawable目录上右键 -> New -> Vector Asset



    2. File -> New -> Vector Asset


    Android项目中如何使用Vector?

    从Android 5.0开始,系统提供了两个Api来完成对Vector的支持:

    1. VectorDrawable
    2. AnimatedVectorDrawable
      VectorDrawable负责矢量图形的显示,而AnimatedVectorDrawable负责基于矢量图形的动画效果。

    与此同时,Android Support Library 23.2.0及之后的版本中,也加入了对Vector的向下兼容,引入了VectorDrawableCompat和AnimatedVectorDrawableCompat。

    只需要引入对应的AppCompat库

    compile 'com.android.support:appcompat-v7:23.2.0' 
    
    VectorDrawable API 7+
    AnimatedVectorDrawable API 11+
    

    另外需要在 build.gradle 文件中新增如下配置:

    //For Gradle Plugin 2.0+
     android {
       defaultConfig {
         vectorDrawables.useSupportLibrary = true
        }
     }
    
    //For Gradle Plugin 1.5 or below
    android {
      defaultConfig {
        // Stops the Gradle plugin’s automatic rasterization of vectors
        generatedDensities = []
      }
      // Flag notifies aapt to keep the attribute IDs around
      aaptOptions {
        additionalParameters "--no-version-vectors"
      }
    }
    

    如何使用VectorDrawable?

    在布局文件中,我们可以针对ImageVIew、ImageButton等控制设置对应的矢量图片。

    如果用的Support包,那只需要通过app:srcCompat=“”属性来替代以前的android:src即可。
    另外记得添加xmlns:app="http://schemas.android.com/apk/res-auto"

    <ImageButton
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      app:srcCompat="@drawable/ic_build_black_24dp"
      tools:layout_editor_absoluteX="11dp"
      tools:layout_editor_absoluteY="225dp"
      android:id="@+id/imageButton"
      android:tint="@color/colorAccent" />
    

    如果使用的是Andorid5.0以上的原生支持,可以直接使用android:src=“”来完成对应的展现。

    如何使用AnimatedVectorDrawable?

    AnimatedVectorDrawable其实是针对Vector里面的各种元素,提供了属性动画的支持方式,从而完成对应的动画效果。

    借用官方文档里的例子,如果采用多个xml来实现:
    VectorDrawable's XML file: vd.xml

    <vector xmlns:android="http://schemas.android.com/apk/res/android"
       android:height="64dp"
       android:width="64dp"
       android:viewportHeight="600"
       android:viewportWidth="600" >
       <group
          android:name="rotationGroup"
          android:pivotX="300.0"
          android:pivotY="300.0"
          android:rotation="45.0" >
          <path
             android:name="vectorPath"
             android:fillColor="#000000"
             android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
       </group>
    </vector>
    

    AnimatedVectorDrawable's XML file: avd.xml

    <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
       android:drawable="@drawable/vd" >
         <target
             android:name="rotationGroup"
             android:animation="@anim/rotation" />
    </animated-vector>
    

    Animator XML file that is used in the AnimatedVectorDrawable's XML file: rotation.xml

    <objectAnimator
       android:duration="6000"
       android:propertyName="rotation"
       android:valueFrom="0"
       android:valueTo="360" />
    

    最后在布局中的使用方法,跟使用VectorDrawable是一样的:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <ImageView
            android:id="@+id/imageview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:srcCompat="@drawable/avd"/>
    
    </android.support.constraint.ConstraintLayout>
    

    当然到此为止还有最重要的一步,否则看到的还只是静态的矢量图形:

    ImageView demoView = (ImageView) findViewById(R.id.imageview);
            if(demoView.getDrawable() instanceof Animatable){
                ((Animatable) demoView.getDrawable()).start();
            }
    

    上面是多xml的例子,相当于矢量图形、动画目标、动画规则是分开的。还有一种方式是用单一的xml文件来描述整个矢量动画资源,需要Build Tools 版本在24及以上:

    <?xml version="1.0" encoding="utf-8"?>
    <animated-vector
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:aapt="http://schemas.android.com/aapt">
        <aapt:attr name="android:drawable">
            <vector xmlns:android="http://schemas.android.com/apk/res/android"
                    android:width="64dp"
                    android:height="64dp"
                    android:viewportWidth="600"
                    android:viewportHeight="600">
                <group
                    android:name="rotationGroup"
                    android:pivotX="300"
                    android:pivotY="300"
                    android:rotation="45.0" >
                    <path
                        android:name="vectorPath"
                        android:fillColor="#000000"
                        android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
                </group>
            </vector>
        </aapt:attr>
        <target android:name="rotationGroup">
            <aapt:attr name="android:animation">
                <objectAnimator
                    android:propertyName="rotation"
                    android:valueFrom="0"
                    android:valueTo="360"
                    android:duration="6000"
                   />
            </aapt:attr>
        </target>
    </animated-vector>
    

    因为AnimatedVectorDrawable改变的是vector中各元素的属性值,所以极大的增添了动画的实现手段及效果。

    不过坑依然有,当API < 21时,扩展包中的AnimatedVectorDrawableCompat会有一些限制:

    Path Morphing (PathType evaluator) Used to morph one path into another path.

    Path Interpolation Used to define a flexible interpolator (represented as a path) instead of the system-defined interpolators like LinearInterpolator.

    Move along path The geometry object can move around, along an arbitrary path, as part of an animation.

    在此限制下,最能发挥效果的pathdata路径动画基本就可以忽略了。至少在发此文章的时候以上限制还在,只能默默期待以后吧。

    总结

    此文只是初略的讲述了Android中矢量图形的基础使用方法,当然深入来讲其实还有很多东西,包括第三方的一些SVG库、矢量动画的一些高级用法等等。在此只是做一个用法的基本记录,同时希望也是借此机会抛砖引玉,给大家提供一个新的处理思路吧。

    原文链接: Android中矢量图形的那些事 - SVG or Vector

    相关文章

      网友评论

        本文标题:Android中矢量图形的那些事 - SVG or Vector

        本文链接:https://www.haomeiwen.com/subject/jvgonttx.html