拥抱SVG:苦恼于图片适配 in Android?

前言

无论是开发 Android 已久的老司机,仍是刚刚上车的新司机,都确定会对一件事情深恶痛绝:图片适配(尤为是在美工不给力的条件下)!为何 Android 手机要有这么多不一样的分辨率? 为何个人图片在这台手机上显示地好好的彻底符合设计图的要求结果换到另外一台手机上就变形了?Oh my god ! html

之前为了解决图片在不一样的分辨率的屏幕上显示不一致的问题,一般咱们会采起两种方式:一是根据不一样的分辨率创建不一样的资源包,而后每一个要用到的图片资源都须要作成多种尺寸的以适配不一样的分辨率;二是干脆直接放一个分辨率比较大的图片,而后钦定 ImageView 的大小,强行把图片塞进去——因为图片的分辨率比较大,因此在大部分机型上也仍是能看的。可是这两种方式都有一些问题,首先不可避免的,都极容易致使 apk 包的体积变大,这个问题在 app 体量比较小的时候可能不会对产品形成什么干扰,可是当体量逐渐变大,这将是个极为让人头疼的问题。另外,用第一种方法完成过图片适配的兄弟都懂,把不一样尺寸的图片改名为符合规范的相同名字并放到它们合适的文件夹里面去简直是一种折磨。。。java

这样的痛苦体验何时才是个头?android

拥抱SVG吧。git

PS:我用 SVG 动画写了一个相似于 Google 2016 I/O 大会上的那个时钟的东西,应该是一个比较好的学习 SVG 在 Android 上的使用的资源,你们能够去关注一下点点 star 提提 issue 哈,地址是:GoogleClock,贴个截图:github

GoogleClock

正文

1,什么是 SVG ? && SVG VS 位图

SVG是指可伸缩矢量图形 (Scalable Vector Graphics),它不一样于传统的位图,不是经过存储图像中每一点的像素值来保存与使用图形,而是经过 XML 文件来定义一个图形,经过一些特定的语法和规则来绘制出咱们所需的图像——一样是使用一张图片,SVG 的方式是事先定义好怎么去画这个图,而后等要用的时候再把它去画出来,而使用传统的位图的话就是已经有了画出来的图,而后要用的时候直接把画好的图拿出来用。这样一来的话咱们就很容易能够分析出它们两种方式之间的优劣之处:app

  • SVG 是在要用图的时候再把图画出来,因此理所固然的在图片显示的时候会花费更多的时间消耗更多的资源。
  • 一样因为上一个缘由, SVG
    并不太适合层次过于复杂细节过于繁多的图片。
  • 位图是事先已经画好的图片,因此适应性必然没有 SVG 好,同一张图片在不一样分辨率下显示会有差别。
  • SVG 的文件里存储了绘制图片的相关信息,因此咱们可以对图片的线条有一个很是清晰的感知,这在作动画的时候特别有用。
  • SVG 没有存储任何图像的像素信息,因此 SVG 的文件体积远小于传统的位图文件。
  • SVG 的文件画出来的图像是矢量图,因此不会存在失真的问题,理论上支持任何级别的缩放。

从上面的分析你们能够发现,在 SVG 的众多优势下, 它的缺点几乎能够忽略不计了(固然,前提是它所耗费的资源不会对用户体验形成较大的影响)——这也正是本文标题 “拥抱SVG” 的由来。svg

2,SVG in Android

既然 SVG 这么好,那么为何当前并无多少 Android 应用是使用了 SVG 图片的呢?由于市场。Android对于 SVG 的支持是从 Android L 开始的,它的 SDK 里面加入了 VectorDrawable , AnimatedVectorDrawable 等类帮助咱们构建 SVG 图形以及动画,而且你能够在 xml 文件里面直接使用 标签绘制 SVG 图像以及 标签为 SVG 图像分配动画。 布局

可是请注意,目前的各类兼容方案,无论是官方出的兼容包仍是各类社区里出现的一些方案,都没有实际上的把它的兼容作到尽善尽美的地步——这意味着,若是不想将就的话,就只能等到市场上基本都是 Android L 或以上的设备的时候,才有可能在生产中大规模的全面的用 SVG 替换位图了。而目前,2016年秋,据友盟统计,Android L 及以上的设备的市场占有率仅有 43.27% ,一半都不到——可是很显然,距离 SVG 在 Android 上发力的时候没有多远了,毕竟目前在售卖的 Android 手机已经基本上都是搭载的 Android L 及以上的系统了,只待老设备被淘汰。学习

3,SVG 的使用

3.1,得到一个 SVG 文件

要使用 SVG ,那么首先咱们确定得有一个 SVG 文件。咱们通常都有两种方式来得到一个 SVG 文件:本身写一个 SVG 文件,或者经过 AI 或一些网站做图以后导出它的 SVG 文件。动画

3.1.1,本身写一个 SVG 文件(Android 中)

前面说过,SVG 文件里面存储的是如何去绘制目标图片的相关信息,因此理论上咱们是能够从 0 开始写一个咱们本身的 SVG 文件的——只要知道它绘制文件的规则,一切皆可绘制。咱们先来看一下一个简单的 SVG 文件:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="132dp"
        android:height="132dp"
        android:viewportHeight="132.0"
        android:viewportWidth="132.0">
    <path
        android:pathData="M50,2 L80.813,2 L80.813,130 L50,130 L50,2 Z"
        android:strokeColor="#e33e2b"
        android:strokeWidth="8" />
</vector>复制代码

这个文件绘制出来的图形是这样的(没错,在编写 SVG 的 XML 文件的时候 Android Studio 是能够预览的,很强大):

绘制出来的图案

能够看到,总的来讲 SVG 文件在 Android 中的载体是一个 <vector/> 标签,而绘制图片的工做是在 <path/> 这个子标签里面作的。咱们先来看一下这两个标签里面最常使用的一些属性,更多的一些属性我会单开一篇博文专门讲这个。

vector:

属性 参数类型 默认值 描述
width dimen 必填属性 图形的实际宽度,可在使用时根据须要再次定义
height dimen 必填属性 图形的实际长度,可在使用时根据须要再次定义
viewportHeight float 必填属性 定义画布的尺寸
viewportWidth float 必填属性 定义画布的尺寸

平时在 <vector/> 标签里面经常使用的属性基本上也就上面这四个了,可是关于这四个属性我想再多讲一些东西,由于我发现目前网上有不少文章对这个的描述都有些问题,容易对刚接触这个的同窗产生误导。首先若是对应到一张具体的图片,XXX.png来说的话,上表中的 widthheight 就至关于 XXX.png 的实际的宽度和长度,可是不一样的是咱们能够在使用它的时候再任意的定义它的新的宽度和长度,甚至比例和原先不同都不要紧,图形并不会所以而失真。而 viewportHeightviewportWidth 这两个属性则是用来肯定 XXX.png 上的坐标系的单位长度的。比方说,像上面的代码的话,viewportHeight 为 132 , heigth 为 132dp ,意思就是图片的长度被分红了 132 份,每一份的长度为 1dp ——这样一来,坐标系的单位长度就有了,也就有了根据坐标来绘制图形的基础。另外,在 Android 上 SVG 的画布上的坐标系是这样的:

坐标系

接下来看一下 <path/> 标签的经常使用属性:

属性 参数类型 默认值 描述
pathData String 画图的核心所在,有必定的语法,根据它来绘制目标图形
strokeColor color 透明 画笔的颜色
name String 这一条path的name,在其余地方能够根据name来找到这一条path
strokeWidth float 0.0 画笔的宽度
fillColor color 透明 用颜色填充绘制过的区域,若是图形是闭合的就直接填充,若是图形不是闭合的那么就将图形的起点和终点相连使其闭合而后填充

基本上知道了上面这些属性就能够经过赋给 pathData 一些值来绘制出一些比较常规的图形了。接下来我讲一下在 pathData 里面绘制图形的一些基本语法:

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

另,上述全部指令大小写都可。大写表示绝对定位,参照全局坐标系;小写表示相对定位,参照父容器坐标系。指令和数据间的空格能够省略。同一指令出现屡次能够只用一个。

如今咱们能够稍微的解读一下上面的那个例子里面的 pathData 是什么意思了。上面的数据是:

M50,2 L80.813,2 L80.813,130 L50,130 L50,2 Z
//你们能够看到,L 指令的后面只有一个坐标,这样的意思就是画的时候以上一笔的终点为起点复制代码

根据咱们的语法,他的意思就是:

  • M 50,2 :将画笔移到(50,2) 坐标处。
  • L 80.813,2 :从 (50,2) 画直线到 (80.813,2)。
  • L 80.813,130 :从 (80.813,2) 画直线到 (80.813,130)。
  • L 50,130 :从 (80.813,130) 画直线到 (50,130)。
  • L 50,2 :从 (50,130) 画直线到 (50,2)。
  • Z :结束这一笔。

咱们不难发现,绘制的过程其实很简单,最后的结果是造成了一个闭合的矩形——和咱们的成型的图像是一致的。

在这里我就不介绍更多的指令的使用了,具体的你们能够参照这篇文章:Android vector标签 PathData 画图超详解。不过其实我认为,根本没有必要去把这些指令彻底的记忆下来,由于在实际的工做中咱们几乎是不会去手写 SVG 文件的,咱们须要掌握的指令其实只有两个: M 和 Z 。由于它们象征着画笔的一笔的开始和结束,在涉及一些和路径轨迹相关的操做的时候咱们能够合理的控制 M 和 Z 来很快速的达到咱们的目的。

3.1.2,获取一个 SVG 文件(Android 中)

在但愿加入 SVG 文件的地方右键一下,而后 new -> Vector Asset :

点一下 Vector Asset ,会弹出另外一个新的弹窗,它是长这样的:

选择 MD icon 或者导入现成的 SVG 文件

图中有两个白色框框起来的部位,上面那个是一个单选框,能够很清晰的知道选第一个是选取 Material Icon (没错,就是这么贴心,AS 自带全部 MD 图标的 SVG 文件),第二个是选取本地 SVG 文件,选好第一个框以后再在下面的白框里面选择具体的哪个文件——这样一来,咱们的目的就完成了,咱们成功的在 AS 里面获得了一个 SVG 的 XML 文件。

在这里可能不少同窗会有一个问题:刚才说了如何将本地的 SVG 文件导入到 AS 中,那么咱们又怎么得到本地的 SVG 文件呢?讲道理,这个问题不该该是咱们考虑的,这个东西应当是美工把制做好的图给咱们,而后咱们直接使用就能够了。可是有的时候咱们本身作小东西并无专业的美工怎么办呢?要么你能够本身去学学 AI 或者 GIMP 等软件的使用方法, 用它们来制做图形而后导成 SVG ,固然这样的话学习成本有点高——不过不要紧,咱们还有低配版的实现方式Method Draw。这是一个在线制做矢量图的网站,能够很方便的将在上面制做的图形导出成 SVG 文件,学习成本至关低,并且能完成咱们大部分的需求,总之我以为还挺好用的。

3.2,开始使用吧!

若是前面的工做都完成了的话,咱们应当是已经有了一个 SVG 的 XML 文件,接下来理所固然的是如何在咱们须要的地方使用它了。那么怎么使用呢?

我想说的是,接下来你彻底能够把它当成咱们引入项目的一张图片来用。好比:

在布局文件中使用 SVG

上图是直接在布局文件中直接引用 SVG 的 XML 文件,代码中的 ic_android_black_24dp 就是已经写好的 SVG 文件。能够看到,直接把它当作一张普通的图片来使用就能够了。固然,咱们也能够在 Java 代码里面来使用 SVG 文件,像下面这样:

mImageView.setImageDrawable(getDrawable(R.drawable.ic_android_black_24dp));复制代码

到这里咱们就已经能够在 Android 中使用 SVG 来做为图片资源了,这样一来不只 Apk 包的体积获得了大大的减少,咱们的图片也具备了任意拉伸而不失真的特性,并且咱们也不再用很是痛苦的去搞图片更名称分包了。

结语

总的来说,我认为 SVG 是有在大部分应用场景下取代传统的位图成为一种更优的图片的解决方案的潜力的,至少在 Android 中是这样。可是因为目前搭载着 Android L 以前的机子还不少,全部不少的 Android 开发人员就将学习 SVG 相关的知识无限期的推迟了,但其实,已是时候了。

另外,这篇文章更多的是一片科普性质的博文,主要是在介绍 SVG 的一些状况,包括它的好处啊,怎么在 Android Studio 上获取导入啊什么的,关于 SVG 在 Android 上的使用只涉及到了一些很皮毛的部分,更多的比较深刻的东西会在后续博文中进一步阐述,敬请期待。

最后,再打一发广告:我用 SVG 动画写了一个相似于 Google 2016 I/O 大会上的那个时钟的东西,应该是一个比较好的学习 SVG 在 Android 上的使用的资源,你们能够去关注一下点点 star 提提 issue 哈,地址是:GoogleClock

相关文章
相关标签/搜索