美文网首页Android 技术收集SVG
浅谈SVG及矢量图在Android的应用

浅谈SVG及矢量图在Android的应用

作者: dfqin | 来源:发表于2018-04-05 20:08 被阅读410次

    1. 图像基础

    图像分为矢量图和栅格图两种,这两张格式最直观的区别是矢量图可以无限放大而不失真,而矢量图放大或缩小就会因为失真而变得模糊,可以参加如下图片。


    对比图

    1.1栅格图

    栅格图也称位图,它由像素点组成,每个像素点分配特定的色值和位置。我们平时生活和工作中遇到的图像大部分都是栅格图,它对图片在空间和亮度上都做了离散化。我们先拿最简单的一张黑白位图举例:


    黑白位图

    假如这个图片是300x300的,即它由300x300个像素点组成,每个像素点要么是黑色的,要么是白色的。

    如果像让图片表达更丰富点,可以使用灰度图,参见下面这张图像处理课中的经典图片。一般灰度图每个像素点用0~255表示灰度等级,0表示白色,255表示黑色,这样的话一张300x300的图片就可以用 unsigned int8[300][300]来表示这张图片。

    灰度图

    那支持彩色图片,也同样原理了,每个像素点支持RGB三原色,三原色可以产生其他颜色。正常情况下RGB每种颜色也是支持0~255个色度。为了支持图片透明,有ARGB格式,前面的A表示透明度,这时一个像素点大小为int32。写程序时有时图片占用内存太大,会把加载时的RGB888改成RGB555,它的原理即让支持256个色度的颜色改为了128个色度,节省了空间牺牲了颜色的饱和度。

    上面讲到的图片放大或者缩小,可以看数字图像处理教程中的公式,放大或者缩小只是对原图像素位置做矩阵运算得到新的位置,对于放大新产生的像素点或者缩小时不是整数位的像素点,需要做插值处理。由此可以知道失真是必然的。

    缩放

    1.2 矢量图

    矢量的定义为既有大小又有方向的几何对象,那矢量图顾名思义为由矢量组成的图像。矢量图由矢量定义的直线和曲线组成,可以放在特定位置使用颜色填充,它基于数学公式计算获得,所以无论放大还是缩小都不会失真。
    可以举个抽象的栗子,图像是个红色的圆形,或者是一个绿色的等边三角形。这样的图像无论你放大多少倍都不会失真的。如下图为字体库导出的矢量图。


    字体库

    根据两种图像的实现原理,不难得出他们的优缺点:矢量图只需要存储函数和一些关键点和方向信息,所以存储空间非常小,对它进行放大缩小旋转等操作也不会有任何失真。由于图像是临时计算出来的,对于运算资源需求大,同时对于颜色描述能力不够。

    2. SVG在android中的使用

    2.1 SVG简介

    SVG全称为Scalable Vector Graphics,是由(W3C)联盟指定的基于XML的矢量图形格式,是一个开放的标准。
    它以<svg>标签开始,以</svg>标签结束。标签可以添加一些属性,也可以嵌套一些其他标签。下面我们可以看几个例子

    <svg width="100%" height="100%" version="1.1">
        <circle cx="100" cy="50" r="40" stroke="black"stroke-width="2" fill="red"/>
    </svg>
    
    圆形
    <svg width="100%" height="100%" version="1.1">
        <polygon points="220,100 300,210 170,250 123,234" style="fill:#cccccc;stroke:#000000;stroke-width:1"/>
    </svg>
    
    多边形
    <svg width="100%" height="100%" version="1.1">
    <path d="M153 334
    C153 334 151 334 151 334
    C151 339 153 344 156 344
    C164 344 171 339 171 334
    C171 322 164 314 156 314
    C142 314 131 322 131 334
    C131 350 142 364 156 364
    C175 364 191 350 191 334
    C191 311 175 294 156 294
    C131 294 111 311 111 334
    C111 361 131 384 156 384
    C186 384 211 361 211 334
    C211 300 186 274 156 274"
    style="fill:white;stroke:red;stroke-width:2"/>
    </svg>
    
    路径

    通过上面的几个例子,基本可以窥视SVG用法,通过一些标签和属性,完成各种图形。查看文档知道它有圆形、矩形、多边形支持一些常规需求,比较复杂的图像可以使用路径(path)来实现。当然复杂的图形,可以使用其他工具去完成然后生成路径。

    2.2 SVG图创建和获取

    1)官方自带Meterial Icon,官方自带了一套图片库,可以满足常用场景需求。系统鼠标选中drawable文件夹, New -> Vector Asset,然后出现

    选择
    上面默认勾选"Material Icon",然后点击Android图标,进入官方自带的SVG图标库:
    图片库
    我们可以选一个图标看一下(点击右边的Preview可以预览图片效果):
    SVG图片
    2)本地导入。上面的提到的勾选框,选择Local file可以导入本地已有的SVG图片或PSD。可以找专业设计师设计图标,也可以从一些图片库上面去下载需要的图标,例如阿里的iconfont(http://iconfont.cn/),还有一些第三方工具例如Vector Magic,将png、jpg等图片格式导出SVG,也有在线导出svg的工具如http://editor.method.ac/

    2.3 在Android中应用

    正常来说,矢量图在Android应用很简单,我们把它当做普通图片来使用就可以了。现在的主要问题是,Android是从5.0版本才开始支持矢量图的,为了适配低版本手机,需要额外做一些工作:
    1)ImageView中矢量图的使用。一般使用兼容包里的AppCompatImageView替换ImageView,在布局中使用“app:srcCompat”代替“app:src”。如果当前的Activity继承了AppCompatActivity,可以直接使用ImageView,编译时会自动替换,不过属性还需要使用“”app:srcCompat。

    <ImageView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:scaleType="fitXY"
            app:srcCompat="@drawable/ic_clear_black_24dp"
            />
    
    1. background中使用矢量图。低版本普通控件background是不支持直接使用矢量图的,对矢量图的支持依赖于StateListDrawable,InsetDrawable,LayerDrawable,LevelListDrawable,RotateDrawable。所以可以通过使用selector来支持矢量图。
    <Button
        android:layout_width="200dp"
        android:layout_height="50dp"
        android:background="@drawable/selector"
        />
    
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:drawable="@drawable/ic_clear_black_24dp" android:state_pressed="true"/>
        <item android:drawable="@drawable/ic_clear_black_24dp"/>
    </selector>
    

    这样还不够,还需要在Activity前面添加如下代码

    static {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    }
    

    跟进源码可以看到只是修改一个静态变量的值,所以图省事可以在application里面添加,这样所有的Activity控件都支持vector了,但是看到它的注释里提到,开启这个flag会增加内存开销,并且影响更新Configuration实例。具体的影响目前还没有测试。
    开启这个这个flag后,android:drawableLeft、RadioButton已经ImageView的src属性都可以正常使用矢量图了。

    3. 矢量动画

    3.1官方矢量动画

    Android提供了AnimatedVectorDrawable来实现矢量动画。它有几个重要方法需要了解:
    start:开始播放动画
    stop:停止动画
    registerAnimationCallback: 注册一个监听者,可以监听到动画开始和结束事件。矢量图形的很多属性都支持动画,最常用的主要是下面三类:

    • 1)属性变换类。主要属性包括alpha、rotation、scaleX、scaleY、translateX、translateY等,这个跟常见的补间动画一样。
    • 2)路径绘制类,这个主要使用path标签的trimPathStart和trimPathEnd属性。它可以按照path的路径逐步画出或者擦除图片。我们可以看下图(盗图:),由path绘制的一个圆形和一个对号,可以使用下面代码实现路径绘制:
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:interpolator="@android:interpolator/linear"
        android:propertyName="trimPathEnd"
        android:valueFrom="0"
        android:valueTo="1"
        android:valueType="floatType" />
    
    路径绘制
      1. 路径变换类,这个主要是指定两个path,系统自动生成动画,从第一个path图形变换为第二个path图形。但是并不是所有的path间都支持这种变换,如果两个path间不支持时系统会抛出异常。这里有一个开源项目可以帮你优化path使其支持路径变换动画。动画代码如下,valueFrom指向动画开始的path,valueTo指向动画变换后的path
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
      android:duration="3000"
      android:propertyName="pathData"
      android:valueFrom="@string/path_begin"
      android:valueTo="@string/path_end"
      android:valueType="pathType"
      android:interpolator="@android:anim/accelerate_interpolator"/>
    
    路径变换 路径变换

    3.2 Lottie,让矢量动画更简单

    上面讲到了官方矢量动画的实现,客观讲使用起来还是有点复杂的,首先要准备相关的矢量图资源,然后再写动画文件,并且根据上面讲到的知识,一些复杂动画实现起来还是很麻烦的。另外一个动画好不容易在Android平台上实现了,iOS上面可能还需要做同样的实现工作,介于以上原因,airbnb实现了一套通用矢量动画方案lottie,可以支持android、iOS、RN和Web。它的思路就是设计同学使用相关工具(例如After Effects)做出矢量动画,导出为一个json文件,然后这个json文件就可以拿来在不同的系统上使用。我们看下Android中的使用:

    • 1)引入库
    compile 'com.airbnb.android:lottie:2.1.0'
    
      1. 使用动画。lottie最低支持到api 16,支持从assets中和res/raw中加载动画资源,官方建议使用raw,因为可以使用R文件静态引用到资源文件,下面一个例子:
    <com.airbnb.lottie.LottieAnimationView
            android:id="@+id/animation_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
    
            app:lottie_rawRes="@raw/hello_world"
            // 或者
            app:lottie_fileName="hello_world.json"
    
            app:lottie_loop="true"
            app:lottie_autoPlay="true" />
    

    就这么简单就完成了矢量动画的显示,当然还有一些其他设置例如停止动画、监听动画等可以参考官方文档
    另外lottie还支持从网络下载矢量动画资源:

    LottieAnimationView animationView = ...
    // This allows lottie to use the streaming deserializer mentioned above.
    JsonReader jsonReader = new JsonReader(new StringReader(json.toString()))
    animationView.setAnimation(jsonReader)
    animationView.playAnimation
    

    使用起来非常简洁,这里有一个网站提供大量动画素材,大家可以参考使用。

    4. 参考

    相关文章

      网友评论

        本文标题:浅谈SVG及矢量图在Android的应用

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