原文地址:http://blog.csdn.net/eclipsexys/article/details/51838119html
http://blog.csdn.net/qq_15545283/article/details/51472458前端
在安卓的发展历程中,因为设备碎片化的原故,谷歌在app中图标的适配上作出一步又一步的改进,大致有这么几个阶段:java
随着Android L的发布,带来了VectorDrawable,矢量图的支持android
Android Studio中配置gradle参数git
//在gradle2.0及以上: android { defaultConfig { vectorDrawables.useSupportLibrary = true }} //在gradle 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" } }
固然,最重要的还有添加appcompat的支持:github
compile 'com.android.support:appcompat-v7:23.4.0'
若是须要在TextView的drawabletop等上边使用,还须要在对应的Activity中加入这个代码:缓存
static { AppCompatDelegate.setCompatVectorFromSourcesEnabled(true); }
而且,咱们要在普通控件上使用Vector,就必须依附于StateListDrawable,InsetDrawable,LayerDrawable,LevelListDrawable,RotateDrawable,代码以下:app
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/icon_shopping"/> </selector>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:drawableLeft="@drawable/shopping_res" android:text="我想要小图标"/>
使用Android Studioeclipse
步骤以下:编辑器
新建:
点击Local SVG 选择路径,并命名drawable文件名字
就在drawable目录生成了以下图
有了静态的Vector图像,就能够在控件中使用了。
这里咱们使用的都是普通的ImageView,好像并非AppcomatImageView,这是由于使用了Appcomat(所在的Activity继承了AppcompatActivity)后,系统会自动把ImageView转换为AppcomatImageView。 (固然你能够在普通Activity中使用 android.support.v7.widget.AppCompatImageView 来直接使用。)
对于ImageView这样的控件,要兼容Vector图像,只须要将以前的android:src属性,换成app:srcCompat便可,示例代码以下所示:
<ImageView android:id="@+id/iv" android:layout_width="wrap_content" android:layout_height="wrap_content" app:srcCompat="@drawable/vector_image"/>
在代码中设置的话,代码以下所示:
ImageView iv = (ImageView) findViewById(R.id.iv); iv.setImageResource(R.drawable.vector_image);
setBackgroundResource也是能够设置Vector的API
Button并不能直接使用app:srcCompat来使用Vector图像,须要经过Selector来进行使用,首先,建立两个图像,用于Selector的两个状态,代码以下所示:
selector1.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0"> <path android:fillColor="#FF000000" android:pathData="M14.59,8L12,10.59 9.41,8 8,9.41 10.59,12 8,14.59 9.41,16 12,13.41 14.59,16 16,14.59 13.41,12 16,9.41 14.59,8zM12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/> </vector>
selector2.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0"> <path android:fillColor="#FF000000" android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/> </vector>
selector.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/selector1" android:state_pressed="true"/> <item android:drawable="@drawable/selector2"/> </selector>
很是简单,只是把普通的Selector中的图像换成了Vector图像而已,接下来,在Button中使用这个Selector便可:
<Button android:id="@+id/btn" android:layout_width="70dp" android:layout_height="70dp" android:background="@drawable/selector"/>
而后运行,若是你认为能够运行,那就是太天真了,都说了是兼容,怎么能没有坑呢,这里就是一个坑……
这个坑其实是有历史渊源的,Google的一位开发者在博客中写到:
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’s src属性。
RadioButton的Button一样能够定义,代码以下所示:
<RadioButton android:layout_width="50dp" android:layout_height="50dp" android:button="@drawable/selector"/>
动态Vector(即AnimationVerctorDrawable)才是Android Vector Drawable的精髓所在
动态的Vector须要经过animated-vector标签来进行实现,它就像一个粘合剂,将控件与Vector图像粘合在了一块儿,一个基础的animated-vector代码以下所示:
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/XXXXX1"> <target android:name="left" android:animation="@animator/XXXXX2"/> </animated-vector>
实际上这里面只有两个重点是须要关注的,XXXXX1和XXXXX2。一个具体的示例以下所示:
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/ic_arrow"> <target android:name="left" android:animation="@animator/anim_left"/> <target android:name="right" android:animation="@animator/anim_right"/> </animated-vector>
这里表示目标图像是drawable/ic_arrow,对left、right分别使用了anim_left、anim_right动画。这里的name属性,就是在静态Vector图像中group或者path标签的name属性。
animated-vector标签在如今的Android Studio中其实是会报错的,但这个并不影响编译和运行,属于Android Studio的Bug。
目标图像
XXXXX1是目标Vector图像,也就是静态的Vector图像,例如:
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="120dp" android:height="120dp" android:viewportHeight="24.0" android:viewportWidth="24.0"> <group android:name="left"> <path android:fillColor="#FF000000" android:pathData="M9.01,14L2,14v2h7.01v3L13,15l-3.99,-4v3"/> </group> <group android:name="right"> <path android:fillColor="#FF000000" android:pathData="M14.99,13v-3L22,10L22,8h-7.01L14.99,5L11,9l3.99,4"/> </group> </vector>
能够发现,这里的Vector图像比以前咱们看见的要多了一个group标签。group标签的做用有两个:
对Path进行分组,因为咱们后面须要针对Path进行动画,因此可让具备一样动画效果的Path在同一个Group中 拓展动画效果,单个的path标签是没有translateX和translateY属性的,所以没法使用属性动画来控制path translateY,而group标签是有的,因此咱们须要先将相关的path标签元素包裹在一个个的group标签中.
动画效果
XXXXX2实际上就是模板要实现的动画,动画效果实际上就是基础的属性动画,例如:
anim_left.xml
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:interpolator="@android:interpolator/anticipate_overshoot" android:propertyName="translateX" android:repeatCount="infinite" android:repeatMode="reverse" android:valueFrom="0" android:valueTo="-10" android:valueType="floatType"/>
anim_right.xml
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:interpolator="@android:interpolator/anticipate_overshoot" android:propertyName="translateX" android:repeatCount="infinite" android:repeatMode="reverse" android:valueFrom="0" android:valueTo="10" android:valueType="floatType"/>
在代码中使用
ImageView imageView = (ImageView) findViewById(R.id.iv);
AnimatedVectorDrawableCompat animatedVectorDrawableCompat = AnimatedVectorDrawableCompat.create(
this, R.drawable.square_anim ); imageView.setImageDrawable(animatedVectorDrawableCompat); ((Animatable) imageView.getDrawable()).start();
动态Vector兼容性问题
向下兼容问题
一说到兼容,就不得不提到坑,几乎全部的为了兼容而作的改动,都会留下一些不可填满的坑,动态Vector动画也不例外,虽然Google已经对Vector图像进行了Android 2.1以上的兼容,但对于动态Vector动画,仍是有不少限制的,例如:
Path Morphing,即路径变换动画,在Android pre-L版本下是没法使用的。 Path Interpolation,即路径插值器,在Android pre-L版本只能使用系统的插值器,不能自定义。 Path Animation,即路径动画,这个通常使用贝塞尔曲线来代替,因此没有太大影响。
向上兼容问题
除了在低版本上的兼容性问题,在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便可。
抽取string兼容问题
开发者有时候为了代码简洁可能会把Vector图像中的pathData放到string.xml中,而后在Vector图像中引用string。
但这种方式若是经过生成png来兼容5.0如下机型的话,会报pathData错误,编译器不会去读取string.xml,只能把pathData写到Vector图像中,动画文件中也是同样,这也是为了兼容作出的牺牲吗,不得而知。
其它兼容问题
其它很是奇怪、诡异、不能理解的兼容性问题,只能经过版本文件夹的方式来进行兼容了,例如drawable-v21和drawable,分别建立两个文件名相同的资源在两个文件夹下,这样在21以上版本,会使用drawable-v21的资源,而其它会使用drawable下的资源。
所谓Vector动画进阶,实际上就是在利用ObjectAnimator的一些属性,特别是trimPathStart、trimPathEnd这两个针对Vector的属性(要注意pathData属性不兼容pre-L)。
这两个属性的官方文档以下所示:
android:trimPathStart The fraction of the path to trim from the start, in the range from 0 to 1. android:trimPathEnd The fraction of the path to trim from the end, in the range from 0 to 1. android:trimPathOffset Shift trim region (allows showed region to include the start and end), in the range from 0 to 1.
其实很简单,就是一个图像的截取,设置一个比例便可,即当前绘制多少比例的图像,其他部分不绘制,Start和End分别就是从PathData的Start和End开始算,你们参考几个例子就能理解了。
Path Morph动画是Vector动画的一个高级使用,说到底,也就是两个PathData的转换,可是这种转换并非为所欲为的,对于两个PathData,它们能进行Path Morph的前提是,它们具备相同个数的关键点,即两个路径的变换,只是关键点的坐标变化,掌握了这一个基本原理,实现Path Morph就很是容易了。
解释一下。
Bitmap的绘制效率并不必定会比Vector高,它们有必定的平衡点,当Vector比较简单时,其效率是必定比Bitmap高的,因此,为了保证Vector的高效率,Vector须要更加简单,PathData更加标准、精简,当Vector图像变得很是复杂时,就须要使用Bitmap来代替了 Vector适用于ICON、Button、ImageView的图标等小的ICON,或者是须要的动画效果,因为Bitmap在GPU中有缓存功能,而Vector并无,因此Vector图像不能作频繁的重绘 Vector图像过于复杂时,不只仅要注意绘制效率,初始化效率也是须要考虑的重要因素 SVG加载速度会快于PNG,但渲染速度会慢于PNG,毕竟PNG有硬件加速,但平均下来,加载速度的提高弥补了绘制的速度缺陷。
Google的这个视频中,已经对Vector的效率问题作了解释,能够参考下:
https://www.youtube.com/watch?v=wlFVIIstKmA&feature=youtu.be&t=6m3s
SVG,即Scalable Vector Graphics 矢量图,这种图像格式在前端中已经使用的很是普遍了,详见WIKI:https://en.wikipedia.org/wiki/Scalable_Vector_Graphics
Vector,在Android中指的是Vector Drawable,也就是Android中的矢量图,详见:https://developer.android.com/reference/android/graphics/drawable/VectorDrawable.html
所以,能够说Vector就是Android中的SVG实现,由于Android中的Vector并非支持所有的SVG语法,也没有必要,由于完整的SVG语法是很是复杂的,但已经支持的SVG语法已经够用了,特别是Path语法,几乎是Android中Vector的标配,详细能够参考:http://www.w3.org/TR/SVG/paths.html
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 5.0发布的时候,Google提供了Vector的支持。Vector Drawable相对于普通的Drawable来讲,有如下几个好处:
1. Vector图像能够自动进行适配,不须要经过分辨率来设置不一样的图片 2. Vector图像能够大幅减小图像的体积,一样一张图,用Vector来实现,可能只有PNG的几十分之一 3. 使用简单,不少设计工具,均可以直接导出SVG图像,从而转换成Vector图像 4. 功能强大,不用写不少代码就能够实现很是复杂的动画 5. 成熟、稳定,前端已经很是普遍的进行使用了
Vector图像刚发布的时候,是只支持Android 5.0+的,对于Android pre-L的系统来讲,并不能使用,因此,能够说那时候的Vector并无什么卵用。不过自从AppCompat 23.2以后,Google对p-View的Android系统也进行了兼容,也就是说,Vector可使用于Android 2.1以上的全部系统,只须要引用com.android.support:appcompat-v7:23.2.0以上的版本就能够了,可是有不少坑,如上。