Android Vector(Vector/Vector动画)
本文由 Luzhuo 编写,转发请保留该信息.
原文: https://blog.csdn.net/Rozol/article/details/79743079
AppCompat23.2 增加了对Vector(矢量图)的全版本兼容
静态 Vector 支持 Android2.1+
动态 Vector 支持 Android3.0+ (属性动画 Android3.0+ (api>=11))
动态 部分不兼容AndroidL(5.0)以下 (path Morphing)
Vector矢量图简介
PNG(位图) / SVG(矢量图) / Vector差异
- SVG:
- 前端中使用, 是一套语法规范
- 工具:
- SVG Editor(在线): http://editor.method.ac
- 资源:
- iconfont(阿里巴巴): http://www.iconfont.cn/
- flaticon: https://www.flaticon.com
- Vector: Android中使用
- 工具:
- SVG2Android(在线 / SVG转Vector): inloop.github.io/svg2android
- Android Studio Vector Asset: 右键drawable -> New -> Vector Asset -> Material Icon(自带svg图) / Local file(本地的svg图)
- 工具:
- Vector只实现了SVG语法的Path标签
- 大小比较: 5,755B(png) > 2,696(svg) > 1,626(vector)
Vector常用语法:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:name="color"
android:fillColor="#FF000000"
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96z"/>
</vector>
标签:
- width: Vector的宽
- height: Vector的高
- viewportWidth: 把width分成多少等份
- viewportHeight: 把height分成多少等份
- name: 起个名字
- fillColor: 填充颜色
- strokeAlpha: 线条alpa
- strokeColor: 线条颜色
- strokeWidth: 线条宽度
- strokeLineCap: 线帽 round/square
- pathData: 路径, 根据路径绘制语法进行绘制
路径绘制语法:
- M = moveto(M X,Y): 画笔移动到指定位置
- L = lineto(L X,Y): 画直线到指定位置
- Z = closepath(): 关闭路径
- H = horizontal lineto(H X): 画水平线到指定X轴位置
- V = vertical lineto(V Y): 画垂直线到指定Y轴位置
- Q = quadratic Belzier curve(Q X,Y,ENDX,ENDY): 2阶贝塞尔曲线
- C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY): 3阶贝赛尔曲线
VectorDrawable
兼容性:
- 只兼容minSDK >= 21 (AndroidL 5.0)
- Gradle Plugin 1.5 增加了5.0以下版本的兼容
- api >=21 使用原生
- api <= 21 将Vector转换成PNG
AppCompat23.2 增加了对Vector的全版本兼容
- 静态Vector支持Android2.1+
- 动态Vector支持Android3.0+ (属性动画 Android3.0+ (api>=11))
动态VectorDrawable的兼容问题(唯一无法向下兼容的):
- path Morphing (路径转换动画 □ -> ○)
- 在Android L 以上需要代码配置(demo中start的代码不同)
- 在Android L 以下无法使用
- path Interpodation (路径插值器)
- 在Android L 只能使用系统地插值器(不能自定义)
- 不支持从Strings.xml中读取
- path Morphing (路径转换动画 □ -> ○)
VectorDrawable使用场景
- Vector(矢量图) vs Bitmap(位图)
- Vector比较简单时,效率高, Vector非常复杂时, Bitmap效率高
- Vector适用于icon / button / imageview的图标等小的icon, 或者动画效果; Bitmap在GPU中有重绘缓存功能(Vector没有), 能做频繁的重绘(Vector不能).
- vector加载速度 > png, 渲染速度 < png.
静态使用
准备工作
配置module的 build.gradle 文件
android { // ... defaultConfig{ // ... vectorDrawables.useSupportLibrary = true } } dependencies{ // ... // > 23.2 compile 'com.android.support:appcompat-v7:24.2.1' }
配置project的 build.gradle 文件
buildscript{ dependencies{ // > 2.1 classpath 'com.android.tools.build:gradle:2.2.0' } }
创建适量图, 放到
drawable
目录下.- (自己编写矢量图, 或者从外部导入svg图, 导入步骤见 Vector: Android中使用)
在ImageView中使用
app:srcCompat="@drawable/cards"
引用- 如果是有状态属性的控件(如:Button), 则通过
android:background + selector
方式引用
- 如果是有状态属性的控件(如:Button), 则通过
在 布局文件中 含有 vector矢量图 的 Activity 还需要添加一句以下代码
static { AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); }
Vector的放大(缩小同理)
布局: ImageView一个使用默认(24dp * 24dp)大小, 一个使用限定大小
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" app:srcCompat="@drawable/static_cards" /> <ImageView android:layout_width="100dp" android:layout_height="100dp" app:srcCompat="@drawable/static_cards" />
效果:
为什么默认大小是(24dp * 24dp), 因为矢量图就设定的这么大
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"> <!-- ... --> </vector>
通过选择器给Button设置图片
布局: 通过
android:background + selector
实现<Button android:layout_width="100dp" android:layout_height="100dp" android:background="@drawable/static_bg_btn"/>
选择器: 给选择器的默认和按压状态, 分别设置一张不同的vector矢量图
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/static_image" android:state_pressed="true" /> <item android:drawable="@drawable/static_cards" /> </selector> <!-- 选择器 -->
效果:
动态使用
准备工作
- 同 静态使用 , 略
vector + 属性动画
布局: 给ImageView设置一个动画粘合剂
<ImageView android:onClick="anim" android:layout_width="100dp" android:layout_height="100dp" app:srcCompat="@drawable/dynamic_move_anim"/>
动画粘合剂(drawable目录下): 用于粘合 属性动画 和 适量图, 使用时会提示要api>21, 不用管该提示, 低版本也可使用
drawable
设置矢量图通过
target
的name
选择 矢量路径 ,并使用animation
粘合一个属性动画.<?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/dynamic_move" > <target android:animation="@animator/dynamic_move_left" android:name="left" /> <target android:animation="@animator/dynamic_move_right" android:name="right" /> </animated-vector>
适量图: 如果要将矢量图里多个路径都粘合动画, 就需要使用
group
标签进行分组<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" 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,-4v3z" /> </group> <group android:name="right"> <path android:fillColor="#FF000000" android:pathData="M14.99,13v-3L22,10L22,8h-7.01L14.99,5L11,9l3.99,4z" /> </group> <!-- group 标签可以对path标签进行分组, 并且group标签中含有path所没有的标签, 并且只有被该标签包裹才能执行动画 --> </vector>
动画: 属性动画里, 使用沿x轴平移动画, 并使用
overshoot
插值器<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:propertyName="translateX" android:valueFrom="0" android:valueTo="10" android:duration="1000" android:repeatCount="infinite" android:repeatMode="reverse" android:interpolator="@android:interpolator/overshoot" /> <?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:propertyName="translateX" android:valueFrom="0" android:valueTo="-10" android:duration="1000" android:repeatCount="infinite" android:repeatMode="reverse" android:interpolator="@android:interpolator/overshoot" />
在Activity里执行属性动画
public void anim(View view){ ImageView imageView = (ImageView) view; Drawable drawable = imageView.getDrawable(); if(drawable instanceof Animatable){ ((Animatable)drawable).start(); } }
效果:
以上是位移属性动画, 如果要变色动画的话, 只需在属性动画里配置颜色相关属性即可: (颜色提示需要 api>=14, 不用管, 低版本也能用)
<?xml version="1.0" encoding="utf-8"?> <!-- strokeColor:线条颜色 fillColor:填充颜色--> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:propertyName="fillColor" android:valueFrom="@android:color/holo_red_dark" android:valueTo="@android:color/darker_gray" android:duration="5000" android:interpolator="@android:interpolator/overshoot" android:valueType="intType"/>
还有截取动画:
<?xml version="1.0" encoding="utf-8"?> <!-- trimPathStart 从头开始截取, trimPathEnd 从尾开始截取 --> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:propertyName="trimPathStart" android:valueFrom="1" android:valueTo="0"/>
当然也支持组合动画:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <objectAnimator android:propertyName="trimPathStart" android:valueFrom="1" android:valueTo="0" android:duration="10000" android:repeatCount="infinite" android:repeatMode="reverse" android:valueType="floatType" /> <objectAnimator android:propertyName="strokeColor" android:valueFrom="@android:color/holo_red_dark" android:valueTo="@android:color/darker_gray" android:duration="10000" android:repeatCount="infinite" android:repeatMode="reverse" android:valueType="intType" /> </set>
还有路径动画, 但是只支持Android5.0+ (api>=21).
<?xml version="1.0" encoding="utf-8"?> <!-- 属性动画 路径变换( □ -> ○ ) --> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:propertyName="pathData" android:valueFrom="M 48,54 L 31,42 15,54 21,35 6,23 25,23 32,4 40,23 58,23 42,35 z" android:valueTo="M 48,54 L 31,54 15,54 10,35 6,23 25,10 32,4 40,10 58,23 54,35 z" android:valueType="pathType" android:duration="3000"/> <!-- 动态VectorDrawable不支持从Strings.xml中读取<pathData>数据,所以将数据拷贝于此 (兼容问题)-->
使用该动画还需要注意的一个问题, 那就是Activity执行该动画也不一样. (其他均相同)
@TargetApi(Build.VERSION_CODES.LOLLIPOP) public void anim(View view){ ImageView imageView = (ImageView) view; // 此处代码不同 AnimatedVectorDrawable drawable = (AnimatedVectorDrawable) getDrawable(R.drawable.dynamic_pathchange_anim); imageView.setImageDrawable(drawable); if(drawable != null) drawable.start(); }
动画里的 valueFrom 是从 vector 里拷贝出来的, 告诉动画从这里开始变. (如果不写的话, 执行时间会不准, 你会发现速度会快很多).
效果: