今天我在看某脑SVG视频和网上查资料时,发现了和某位大佬的写文章的某种巧合(报以微妙的笑容)。因为强迫症,所以我想总结性的抄袭一下。不论是否被发现,一切荣誉归属于大佬。 抄袭对象大佬: #引导: #概念 ## 即Scalable Vector Graphics 可伸缩矢量图形,这种图像格式在前端中已经使用的非常广泛了。详见WIKI: 什么是矢量图像,什么是位图图像?
- 矢量图像:SVG是W3C 推出的一种开放标准的文本式矢量图形描述语言,他是基于XML的、专门为网络而设计的图像格式,SVG是一种采用XML来描述二维图形的语言,所以它可以直接打开xml文件来修改和编辑。
- 位图图像:位图图像的存储单位是图像上每一点的像素值,因而文件会比较大,像GIF、JPEG、PNG等都是位图图像格式。 在android开发中,不能直接使用SVG文件作为图片文件,否则就会这样:
compile 'com.android.support:appcompat-v7:23.2.0'//及以上复制代码
23.2.0及以上的版本就可以了。 注意事项:所谓的兼容并非能够使用SVG了,本质还是生成了PNG图片来使用。
- Vector图像可以自动进行适配,不需要通过分辨率来设置不同的图片,这在我眼里是最好的地方。
- Vector图像可以大幅减少图像的体积,同样一张图,用Vector来实现,可能只有PNG的几十分之一,这从减少apk大小上很有意义,但有大佬发现,其实并不是完全就能减少,当PNG只有十几k,用Vector反而变大了。
- 使用简单,很多设计工具如PhotoShop、Illustrator,都可以直接导出SVG图像,从而转换成Vector图像,够灵活,不用写很多代码就可以实现非常复杂的动画。
- 成熟、稳定,前端已经非常广泛的进行使用了。
Vector 语法简介
Android以一种简化的方式对SVG进行了兼容,这种方式就是通过使用它的Path标签,通过Path标签,几乎可以实现SVG中的其它所有标签,虽然可能会复杂一点,但这些东西都是可以通过工具来完成的,所以,不用担心写起来会很复杂。
- Path指令解析如下所示:
M = moveto(M X,Y) :将画笔移动到指定的坐标位置,相当于 android Path 里的moveTo()L = lineto(L X,Y) :画直线到指定的坐标位置,相当于 android Path 里的lineTo()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):弧线 ,相当于arcTo()Z = closepath():关闭路径(会自动绘制链接起点和终点)复制代码
- 使用原则:
- 坐标轴为以**(0,0)为中心,X轴水平向右**,Y轴水平向下
- 所有指令大小写均可。大写绝对定位,参照全局坐标系;小写相对定位,参照父容器坐标系
- 指令和数据间的空格可以省略
- 同一指令出现多次可以只用一个
注意事项:
- ’M’处理时,只是移动了画笔, 没有画任何东西。
- 关于这些语法,开发者不需要全部精通,而是能够看懂即可,这些path标签及数据生成都可以交给美工工具和网上的网站来实现: 1)可以使用SVG的编辑器来进行SVG图像的编写,例如:http://editor.method.ac/
静态Vector图像
- 生成图片 例如: 我们用as生成一个矩形
复制代码
复制代码
或者代码设置:
ImageView iv = (ImageView) findViewById(R.id.iv); iv.setImageResource(R.drawable.vector_image); iv.setBackgroundResource(R.drawable.vector_image);//setBackgroundResource也是可以设置Vector的API复制代码
注意事项:
- 当我的minSdkVersion 小于21时,不在5.0+,则不能对Vector直接使用app:srcCompat,否则会报错,需要去build.gradle里去设置一下。后面会在兼容里讲【1】。如果是minSdkVersion>=21,当然是不会报错的了。
- 使用的都是普通的ImageView,而没有改成AppcomatImageView,这是因为使用了Appcomat后,系统会自动把ImageView转换为AppcomatImageView。
- 如果是Button,则不能直接使用app:srcCompat来直接使用Vector图像,需要通过Selector来进行使用,首先,创建两个图像,用于Selector的两个状态(写两个SVG的Drawable):
复制代码
非常简单,只是把普通的Selector中的图像换成了Vector图像而已,接下来,在Button中使用这个Selector即可:
复制代码
然后运行时又会发现有个兼容的坑,也在下面讲【2】。
动态Vector
动态Vector才是Android Vector Drawable的重点。 动态的Vector需要通过animated-vector标签来进行实现,它就像一个粘合剂,将控件与Vector图像粘合在了一起,一个基础的animated-vector代码如下所示:
复制代码
举例:
复制代码
设置了初始显示时的“path_tick”Vector图像,然后,target目标是最终达到“anim_path_tick2cross”效果。 注意事项 :
复制代码
path_tick是目标Vector图像,也就是静态的Vector图像。 可以发现,这里的Vector图像有个group标签。group标签的作用有两个:
- 对Path进行分组,由于我们后面需要针对Path进行动画,所以可以让具有同样动画效果的Path在同一个Group中。
- 拓展动画效果,单个的path标签是没有translateX和translateY属性的,因此无法使用属性动画来控制path translateY,而group标签是有的,所以我们需要先将相关的path标签元素包裹在一个个的group标签中。 anim_path_tick2cross代码为为:
复制代码
anim_path_tick2cross实际上就是利用属性动画来实现的动画效果。 android:propertyName属性名,上文里为pathData,指的是path_tick里的
android:pathData="M19.6,7 L10.4,16.2 M4.8,13.4 L9,17.6"复制代码
从 android:valueFrom动画效果到 android:valueTo。 当然,也可以android:propertyName="translationX"这样换别的动画属性。 写好了动态Vector,作为动画,肯定是要开启执行动画。怎么搞?
复制代码
public void anim(View view) {ImageView imageView = (ImageView) view;AnimatedVectorDrawableCompat animatedVectorDrawableCompat = AnimatedVectorDrawableCompat.create(this, R.drawable.path_tick2cross_anim);imageView.setImageDrawable(animatedVectorDrawableCompat); Drawable drawable = imageView.getDrawable(); if (drawable instanceof Animatable) { ((Animatable) drawable).start(); } }复制代码
判断imageView的图片是个动图,即属于Animatable,则强转并启动动画。这样不再需要动态写代码动画来搞了,向美工御姐撒撒娇就好了。 注意事项 :如果是两个SVG进行动画,path元素中从一个形状转变到另一个形状,这两个形状必须满足:要有一致的命令(command)个数(逗号分割开的为命令),并且每个命令的参数个数也必须一致。
版本兼容
VectorDrawableCompat依赖于AAPT的一些功能,它能保持最近矢量图使用的添加的属性ID,以便他们可以在L版本之前被引用。 在Android 5.0之前使用Vector,需要aapt来对资源进行一些处理,这一过程可以在aapt的配置中进行设置,如果没有启用这样一个flag,那么在5.0以下的设备上运行就会发生android.content.res.Resources$NotFoundException。
- 最重要的是添加appcompat的支持,如:
compile 'com.android.support:appcompat-v7:25.3.1'复制代码
同时,确保你使用的是AppCompatActivity而不是普通的Activity。 2. 你需要在项目的build.gradle脚本中,增加对Vector兼容性的支持,代码如下所示: 使用Gradle Plugin 2.0以上:
android { defaultConfig { vectorDrawables.useSupportLibrary = true }}复制代码
使用Gradle Plugin 2.0以下,Gradle Plugin 1.5以上:
android { defaultConfig { // Stops the Gradle plugin’s automatic rasterization of vectors generatedDensities = [] } // Flag to tell aapt to keep the attribute ids around aaptOptions { additionalParameters "--no-version-vectors" }复制代码
这种兼容方式实际上是先关闭AAPT对pre-L版本使用Vector的妥协,即在L版本以上,使用Vector,而在L版本前,使用Gradle生成相应的PNG图片,generatedDensities这个数组,实际上就是要生成PNG的图片分辨率的数组,使用appcompat后就不需要这样了。
First up, this functionality was originally released in 23.2.0, but then we found some memory usage and Configuration updating issues so we it removed in 23.3.0. In 23.4.0 (technically a fix release) we’ve re-added the same functionality but behind a flag which you need to manually enable.复制代码
实际上,他们的这个改动,就影响了类似DrawableContainers(DrawableContainers which reference other drawables resources which contain only a vector resource)这样的类,它的一个典型,就是Selector(StateListDrawable也是)。这个开发者在文中提到的flag,就是下面的这段代码,放在Activity的前面就可以了:
static { AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);}复制代码
开启这个flag后,你就可以正常使用Selector这样的DrawableContainers了。同时,你还开启了类似android:drawableLeft这样的compound drawable的使用权限,以及RadioButton的使用权限,以及ImageView的src属性。 这就是解决了【2】的问题。 4. 动态Vector动画兼容性问题 1) 向下兼容问题 一说到兼容,就不得不提到坑,几乎所有的为了兼容而做的改动,都会留下一些不可填满的坑,动态Vector动画也不例外,虽然Google已经对Vector图像进行了Android 2.1以上的兼容,但对于动态Vector动画,还是有很多限制的,例如: Path Morphing,即路径变换动画,在Android pre-L版本下是无法使用的。 Path Interpolation,即路径插值器,在Android pre-L版本只能使用系统的插值器,不能自定义。 Path Animation,即路径动画,这个一般使用贝塞尔曲线来代替,所以没有太大影响。 2) 向上兼容问题 除了在低版本上的兼容性问题,在L版本以上,也存在兼容性问题,即继承了AppCompatActivity的界面,如果直接设置ImageView的srcCompat,那么Path Morphing动画是无法生效的,因为默认的AppCompatActivity已经默认使用ImageViewCompat给转换了,但是AnimatedVectorDrawableCompat是不支持Path Morphing动画的,所以,在AppCompatActivity界面里面就无效了。 解决办法很简单,即使用代码来给ImageView添加动画:
ImageView imageView = (ImageView) view; AnimatedVectorDrawable morphing = (AnimatedVectorDrawable) getDrawable(morphing); imageView.setImageDrawable(morphing); if (morphing != null) { morphing.start(); }复制代码
**注意:**不要使用AnimatedVectorDrawableCompat即可。 5. 抽取string兼容问题 开发者有时候为了代码简洁可能会把Vector图像中的pathData放到string.xml中,然后在Vector图像中引用string。 但这种方式如果通过生成png来兼容5.0以下机型的话,会报pathData错误,编译器不会去读取string.xml,只能把pathData写到Vector图像中,动画文件中也是一样。 6. 其它兼容问题 其它非常奇怪、诡异、不能理解的兼容性问题,只能通过版本文件夹的方式来进行兼容了,例如drawable-v21和drawable,分别创建两个文件名相同的资源在两个文件夹下,这样在21以上版本,会使用drawable-v21的资源,而其它会使用drawable下的资源。 7. 即时有了兼容方式,但是,还是会有一些Vector动画是不能兼容Android pre-L版本的,只能兼容L+。 ##学习Vector 大佬的Vector动画Demo库,地址如下所示: 这个Demo分为两部分,一部分是可以兼容Android pre-L版本和L+版本的Vector动画,另一部分(通过Actionbar的按钮切换)是只能兼容L+的Vector动画。 注意事项:这个能兼容和不能兼容,是因为Vector动画本身的效果不能在pre-L版本兼容。不是代码没设置。 每个Vector动画,基本都包含四部分内容,即:
Vector:图像资源Animated-vector:动画、图像粘合剂ObjectAnimator:动画资源代码:启动动画复制代码
每个Vector动画通过这四个部分去进行分析,就非常清晰了。 这里展示下Demo的效果图:
- Bitmap的绘制效率并不一定会比Vector高,它们有一定的平衡点,当Vector比较简单时,其效率是一定比Bitmap高的,所以,为了保证Vector的高效率,Vector需要更加简单,PathData更加标准、精简,当Vector图像变得非常复杂时,就需要使用Bitmap来代替了
- Vector适用于ICON、Button、ImageView的图标等小的ICON,或者是需要的动画效果,由于Bitmap在GPU中有缓存功能,而Vector并没有,所以Vector图像不能做频繁的重绘
- Vector图像过于复杂时,不仅仅要注意绘制效率,初始化效率也是需要考虑的重要因素 ##参考
有个加急项目来了,所以留待以后再更进一步研究。