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

之前对矢量图形有所耳闻,但因为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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容