美文网首页Android iOS开发知识库
android图片系列 (2) - 静态 SVG 图片

android图片系列 (2) - 静态 SVG 图片

作者: 前行的乌龟 | 来源:发表于2017-07-02 01:01 被阅读146次

    SVG 图片是一种可支持任意缩放的图片格式,使用 xml 定义,使用 canvas 中 path 路径来完成绘制,和我们传统使用的 BitMap位图有很大的区别。

    SVG 在前端早就普及了,在android 中是 google 是在5.0之后开始支持的,14年出来之后兼容是个大问题,随着2016.2 V7包 23.2.0版本的发布才算是有个相对完善的兼容使用方案。

    SVG 的概念我就不写了,拿来主义啦,原文: Android Vector曲折的兼容之路


    不瞎逼逼,我们先来看一看 android 中的 SVG 矢量图是个什么东东

    Snip20170626_1.png

    看到没有,这就是一个 SVG 矢量图片,就是一个 xml 文件,右边是预览,先说下,这东西的好处:缩放不失真,体积小。这一个 SVG 图片只有970个字节...强大吧,比 png 格式的图片强的没边了吧,png 我们还得适配,做多套,然后一个一个改名字复制到工程里,有了 SVG 妈妈再也不担心我们写作业啦...

    解析 SVG xml 文件的一些参数

    这里需要解释下这里的几个标签:

    • android:width \ android:height:定义图片的宽高

    • android:viewportHeight \ android:viewportWidth:定义图像被划分的比例大小,例如例子中的500,即把200dp大小的图像划分成500份,后面Path标签中的坐标,就全部使用的是这里划分后的坐标系统。
      这样做有一个非常好的作用,就是将图像大小与图像分离,后面可以随意修改图像大小,而不需要修改PathData中的坐标。

    • android:fillColor:PathData中的这些属性就不详细讲了,与Canvas绘图的属性基本类似。

    这里有一分详细的属性说明:

    <vector>                   定义这个矢量图
        android:name           矢量图的名字
        android:width          矢量图的内部(intrinsic)宽度,支持所有Android系统支持的尺寸,通常使用dp
        android:height         矢量图的内部(intrinsic)高度
        android:viewportWidth  矢量图视图的宽度,视图就是矢量图path路径数据所绘制的虚拟画布
        android:viewportHeight 矢量图视图的高度
        android:tint           矢量图的tint颜色。默认是没有tint颜色的
        android:tintMode       矢量图tint颜色的Porter-Duff混合模式,默认值为src_in。(src_in,src_over,src_atop,add,screen,multiply) 
        android:autoMirrored   设置当系统为RTL(right-to-left)布局的时候,是否自动镜像该图片。比如阿拉伯语。
        android:alpha          该图片的透明度属性
    
    <group>                    设置路径做动画的关键属性的
        android:name           定义group的名字
        android:rotation       定义该group的路径旋转多少度
        android:pivotX         定义缩放和旋转该group时候的X参考点。该值相对于vector的viewport值来指定的。
        android:pivotY         定义缩放和旋转该 group 时候的Y参考点。该值相对于vector的viewport值来指定的。
        android:scaleX         定义X轴的缩放倍数
        android:scaleY         定义Y轴的缩放倍数
        android:translateX     定义移动X轴的位移。相对于vector的viewport值来指定的。
        android:translateY     定义移动Y轴的位移。相对于vector的viewport值来指定的。
    
    <path>
        android:name           定义该path的名字,这样在其他地方可以通过名字来引用这个路径
        android:pathData       和SVG中d元素一样的路径信息。
        android:fillColor      定义填充路径的颜色,如果没有定义则不填充路径
        android:strokeColor    定义如何绘制路径边框,如果没有定义则不显示边框
        android:strokeWidth    定义路径边框的粗细尺寸
        android:strokeAlpha    定义路径边框的透明度
        android:fillAlpha      定义填充路径颜色的透明度
        android:trimPathStart  从路径起始位置截断路径的比率,取值范围从0到1;注意从一半到起始动画为from-0.5-to-0
        android:trimPathEnd    从路径结束位置截断路径的比率,取值范围从0到1;注意从一半到结束动画为from-0.5-to-1.0
        android:trimPathOffset 设置路径截取的范围,取值范围从0到1
        android:strokeLineCap  设置路径线帽的形状,取值为 butt, round, square.
        android:strokeLineJoin 设置路径交界处的连接方式,取值为 miter,round,bevel.
        android:strokeMiterLimit 设置斜角的上限
    
    <clip-path>                定义当前绘制的剪切路径。注意,clip-path 只对当前的 group 和子 group 有效
        android:name           定义clip-path的名字
        android:pathData       android:pathData的取值一样。
    
    根元素<vector>上有两个宽高设置,其中viewport是指矢量图里面的画布大小,而android:width和android:height是指该矢量图所对应的VectorDrawable的大小。
    
    关于tintMode:  
    在5.0以后我们就可以为bitmap或者是9-patch定义一个透明的遮罩。BitmapDrawable和NinePatchDrawable使用setTint()方法。
    而在xml文件中使用android:tint和android:tintMode这两个属性。
    
    注意点:使用android:tint指定颜色时一定要带透明度。#50ff00ff也就是说是8位的色值而不是6位的。
    
    属性说明: 
    android:tint: 设置的是颜色 
    android:tintMode:设置的是类型(src_in,src_over,src_atop,add,screen,multiply)
    
    类型说明: 
    src_in 只显示设置的遮罩颜色。 相当于遮罩在里面。 
    src_over遮罩颜色和图片都显示。相当于遮罩在图片上方。(特别是色值带透明度的) 
    src_atop遮罩在图片上方 
    multiply 混合色遮罩 
    screen 
    add 混合遮罩,drawable颜色和透明度。
    

    好了下面开始介绍 SVG 啦


    概念

    首先,需要讲解两个概念——SVG和Vector。

    SVG,即Scalable Vector Graphics 矢量图,这种图像格式在前端中已经使用的非常广泛了

    Vector,在Android中指的是Vector Drawable,也就是Android中的矢量图

    因此,可以说Vector就是Android中的SVG实现,因为Android中的Vector并不是支持全部的SVG语法,也没有必要,因为完整的SVG语法是非常复杂的,但已经支持的SVG语法已经够用了,特别是Path语法,几乎是Android中Vector的标配

    Vector语法简介

    Android以一种简化的方式对SVG进行了兼容,这种方式就是通过使用它的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 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():关闭路径

    使用原则:

    坐标轴为以(0,0)为中心,X轴水平向右,Y轴水平向下
    所有指令大小写均可。大写绝对定位,参照全局坐标系;小写相对定位,参照父容器坐标系
    指令和数据间的空格可以省略
    同一指令出现多次可以只用一个
    注意,’M’处理时,只是移动了画笔, 没有画任何东西。 它也可以在后面给出上同时绘制不连续线。
    关于这些语法,开发者需要的并不是全部精通,而是能够看懂即可,其它的都可以交给工具来实现。

    这里有一篇 Android vector标签 PathData 画图超详解 详细描述了 SVG 中 path 的绘制


    好了概念性的东西说完了,我们来看看

    SVG 的使用分2种,一种是静态 SVG 矢量图,就是本文的主角,本章节主要谈论的东西,另一种是 SVG 矢量动画,是SVG 的高级应用,是给静态 SVG 加上objectAnimator 动画,应用的很广泛,是实现 android icon 动态交互的核心做法。

    上面的SVG 图大家都看到了,我们就是写一个 xml 的文件,里面承载的标签都是描述如何绘制我们想要的图案的,画布大小,颜色,路径等,然后交给系统去绘制。

    现在让我们来看看 SVG 在 andorid 中如何应用,如何兼容5.0以下版本。

    SVG 图片的使用

    SVG 虽然早早就在前端使用了,但是 android 上开始支持 SVG 的使用还是从5.0开始的,在5.0以上系统的使用很简单,和之前一样使用 PNG 图片一样

    首先 android 中的 SVG 图片的承载方式是一个 xml 文件,所以UI 给我们的 SVG 图片是不能直接使用的,这里 google 给我们提供加载方式

    Android studio 在 2.3.3 的版本中可以直接使用 svg,新建一个 SVGDemo项目,新建 Vector Asset 文件:app-> main -> New -> Vector Asset 如图所示:

    我们选择 Local File 就是选择本地svg文件进行导入,对文件命名后点击 Next ->Finish 在 drawable目录 下就添加了一个.xml的文件

    好了这样一个 svg 图片我们算是加入到我们的工程里里了,可以直接使用了。当然在此之前我们把 SVG 图片放在那个 drawable 文件夹呢。对于这个问题就要说一下了:

    有一点需要解释一下,svg 矢量图文件我们放在drawable 根目录即可。android 系统不会根据你把 svg 矢量图存放在不同的 drawable 文件夹,对图片进行分辨率上的缩放,因此我们不用像使用 PNG 图片时准备多套图片了。我们导入 SVG 图片默认存放的地址就是 drawable根目录,所以我们就放这里就好了,当然也可以自己写SVG 图片,都是 xml 的,自己写完 path 路径后都是可以查看预览的,一般也不会自己写,都是UI 的活。

    这样就 ok啦,5.0以上的系统SVG你就像一般 png 图片一样使用就好啦,你可以试一下。


    5.0 以下系SVG图片的使用

    前置说明

    从Gradle Plugin 1.5开始,Google支持了一种兼容方式,即在Android L之上,使用Vector,而在L之下,则使用Gradle将Vector生成PNG图像。

    这是目前我能找到最优秀的解释了,我们从中可以看到,SVG 矢量图对于 android系统来说完全是一套新的体系,传统的 PNG 图片android 系统都会维护一个全局的内存缓存,我们使用 R.id方式给 imageview 设置图片都是通过索引来实现的。

    但是 SVG矢量图在 Android系统中并不会维护一份全局的内存缓存,每次使用都会新创建bitmap 对象。这样来看呢,SVG 矢量图在渲染上比传统 PNG 图片效率要低,毕竟没有缓存这是个大问题,但是换过来想有了缓存还怎么做到支持无限缩放呢,这是个驳论,在后面我们会讨论下 SVG的性能问题。

    google 在 gradle 1.5以上版本实现了基本的对5.1以下版本对 SVG 兼容,但是这种把 SVG 转换为 png 的方式并不是我们所需要的,要是在低版本上不能实现大部分 SVG 的功能,那么对于我们开发来说就没什么意义了,因为这设计到我们对于 SVG 矢量动画的使用,我们之所以在 android 上使用 SVG 图片,一半的目的都是为了 SVG矢量动画来的,这能够更好,更简单的实现 android 操作交互,不光高版本,低版本我们也是要求能够实现的,不管什么理由,我们都不可能为了不同版本实现多套代码。SVG 的兼容麻烦就在这里,我们不能使用系统默认的方式,需要自己手动实现兼容。

    兼容实现

    终于到大家最关系的问题啦,现在5.0一下手机保有量还有30%呢,没办法啊,兼容肯定要做的啦,这里吐槽下,android 5.0的变化之大真是超乎人们想象啊

    这个问题大家不用担心,也不用去 github 找啦,google已经为大家准备好了,各位看官请看

    首先我们需要万能的 V7 兼容包

     compile 'com.android.support:appcompat-v7:25.1.0'
    ```
    
    V7包导入之后呢,我们需要在页面所在的 module 的 gradle 中添加一行声明
    
    ```java
     defaultConfig {
            vectorDrawables.useSupportLibrary = true
        }
    ```
    
    然后我们就可以使用啦,当然啦,都是用 V7兼容包啦,那么平时使用的 imageview 肯定就不行拉,我们需要使用兼容控件啦 AppCompatImageView,不习惯使用兼容控件的小伙伴注意啦,为了使用5.0之后的效果,兼容控件还是少不了的哈。还有我们的 activity 必须继承 AppCompatActivity
    
    ![Snip20170704_12.png](https://img.haomeiwen.com/i1785445/f5edbc3de91a7601.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    ```java
     <android.support.v7.widget.AppCompatImageView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_centerInParent="true"
          app:srcCompat="@drawable/ic_android_black_24dp"
        />
    ```
    
    最后要是有崩溃的小伙伴,请在 activity 页面加入这行:
    
    ```java
    static {
            AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
        }
    ```
    
    好了,SVG 的兼容实现做到这里你以为就完了吗,还有坑在后面等着呢!话说做兼容都会留坑,总有兼容留意不到的地方,这些地方就需要我们熟知了。具体到 SVG 的兼容程度,我们就要说上那么一说了。
    
    我们使用 AppCompatActivity 后,会自动把我们使用的 imageview 转换成 appCompatImageView,当然我还是推荐大伙直接使用 AppCompatImageView,你若是要在 AppCompatActivity 使用 imageview的话,src 这个参数是不行的,你还是要这样使用  app:srcCompat="@drawable/ic_android_black_24dp",坑爹不,所以我推荐大会还是直接使用 AppCompatImageView。
    
    另外除了 AppCompatImageView 的 srcCompat 属性可以直接使用 SVG 资源外,
    在其他地方我们是不能直接使用 SVG 资源的,必要要给 SVG 图片资源添加一个容器,可以是:StateListDrawable,InsetDrawable,LayerDrawable,LevelListDrawable,RotateDrawable,selector。 这是 google 写 SVG 兼容这块的程序员亲自说的,所以大伙要注意。
    
    ```java
    // 这是一个 selector 文件
    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item android:drawable="@drawable/ic_android_black_24dp"></item>
    </selector>
    
    // @drawable/ic_android_black_24dp 这个资源是一个 SVG 图片资源
    ```
    
    任何兼容都不完美,里面肯定会有不少坑,所以老版本赶紧淘汰吧,写个代码真闹心!
    
    ***
    
    ####参考资料:
    
    * [Android中使用SVG](http://blog.csdn.net/anyanyan07/article/details/72594108)
    
    * [SVG在Android中的使用](http://www.jianshu.com/p/49a0ec031bde)

    相关文章

      网友评论

        本文标题:android图片系列 (2) - 静态 SVG 图片

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