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用法,通过一些标签和属性,完成各种图形。查看文档知道它有圆形、矩形、多边形支持一些常规需求,比较复杂的图像可以使用路径(path)来实现。当然复杂的图形,可以使用其他工具去完成然后生成路径。
2.2 SVG图创建和获取
1)官方自带Meterial Icon,官方自带了一套图片库,可以满足常用场景需求。系统鼠标选中drawable文件夹, New -> Vector Asset,然后出现
上面默认勾选"Material Icon",然后点击Android图标,进入官方自带的SVG图标库:
我们可以选一个图标看一下(点击右边的Preview可以预览图片效果):
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。
- background中使用矢量图。低版本普通控件background是不支持直接使用矢量图的,对矢量图的支持依赖于StateListDrawable,InsetDrawable,LayerDrawable,LevelListDrawable,RotateDrawable。所以可以通过使用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绘制的一个圆形和一个对号,可以使用下面代码实现路径绘制:
-
- 路径变换类,这个主要是指定两个path,系统自动生成动画,从第一个path图形变换为第二个path图形。但是并不是所有的path间都支持这种变换,如果两个path间不支持时系统会抛出异常。这里有一个开源项目可以帮你优化path使其支持路径变换动画。动画代码如下,valueFrom指向动画开始的path,valueTo指向动画变换后的path
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'
-
- 使用动画。lottie最低支持到api 16,支持从assets中和res/raw中加载动画资源,官方建议使用raw,因为可以使用R文件静态引用到资源文件,下面一个例子:
就这么简单就完成了矢量动画的显示,当然还有一些其他设置例如停止动画、监听动画等可以参考官方文档
另外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. 参考
- http://www.w3school.com.cn/svg/svg_path.asp
- https://www.jianshu.com/p/0555b8c1d26a
- http://blog.csdn.net/dick_zeng/article/details/72473591
- http://blog.csdn.net/anyanyan07/article/details/72594108
- https://www.jianshu.com/p/4707a4738a51
- https://www.cnblogs.com/zhangmiao14/p/7515760.html