矢量图形VectorDrawable

与水波图形RippleDrawable一样,矢量图形VectorDrawable也是Android5.0之后新增的图形类。矢量图不同于一般的图形,它是由一系列几何曲线构成的图像,这些曲线以数学上定义的坐标点连接而成。具体到实现上,则需开发者提供一个xml格式的矢量图形定义,然后系统根据矢量定义自动计算该图形的绘制区域。因为绘图结果是动态计算得到,所以不管缩放到多少比例,矢量图形都会一样的清晰,不像位图那样拉大后会变模糊。

矢量图形的xml定义有点复杂,其结构可分为三个层次:根标签、组标签、路径标签。

根标签vector

首先是vector标签,它表示当前定义的是一个完整的矢量图形。该标签支持的主要属性说明如下:
android:name:指定矢量图形的名称。
android:width:指定矢量图形的默认宽度,一般使用dp数值。如果在layout布局文件中将ImageView的layout_width设置为wrap_content,同时src设置为该矢量图形,则ImageView控件的宽度就是此处的android:width。
android:height:指定矢量图形的默认高度,一般使用dp数值。
android:viewportWidth:指定视图空间的宽度,即虚拟坐标系的宽度,后续路径的坐标信息都位于该视图空间之内。
android:viewportHeight:指定视图空间的高度,即虚拟坐标系的高度。
android:alpha:指定矢量图形的的透明度,取值为0.0到1.0。

这里要注意width/height与viewportWidth/viewportHeight两组宽高的区别,前者指的是矢量图形被外部世界观察到的尺寸大小,故而采用了带dp单位的绝对数值;而后者指的是矢量图形为内部几何路径所参照的空间范围,故而采用了不带单位的相对数值,正因为矢量图形中的几何路径以相对坐标来标记,所以不管矢量图形缩放到多少比例,其内部的几何形状也会按同样比例缩放。

组标签group

然后是group标签,它定义了一组路径的共同行为(如一起旋转、一起缩放、一起平移等等)。该标签支持的主要属性说明如下:
android:name:指定分组对象的名称。
android:pivotX:指定旋转中心点的横轴坐标。
android:pivotY:指定旋转中心点的纵轴坐标。
android:rotation:指定分组对象的旋转角度。
android:scaleX:指定分组对象在横轴上的缩放比例。取值0.5表示缩小一半,取值2.0表示放大一倍。
android:scaleY:指定分组对象在纵轴上的缩放比例。
android:translateX:指定分组对象在横轴上的平移距离。
android:translateY:指定分组对象在纵轴上的平移距离。

路径标签path

最后是path标签,它定义了一个路径的几何描述,既可以表示一根曲线,也可以表示一块平面区域。该标签支持的主要属性说明如下:
android:name:指定几何路径的名称。
android:pathData:指定几何路径的数据定义。数据格式需符合SVG标准。
android:fillColor:指定平面区域的颜色。若不指定,则不绘制平面区域。
android:fillAlpha:指定平面区域的透明度。
android:strokeColor:指定曲线的颜色。若不指定,则不绘制曲线颜色。
android:strokeWidth:指定曲线的宽度。
android:strokeAlpha:指定曲线的透明度。
android:strokeLineCap:指定曲线的首尾外观。取值说明有三个:butt(默认值,直线边缘)、round(圆形边缘)、square(方形边缘)。
android:strokeLineJoin:指定两条曲线相交的边角外观。取值说明有三个:miter(默认值,锐角)、round(圆角)、bevel(钝角)。
android:trimPathStart:指定几何路径从哪里开始绘制。取值为0.0到1.0,比如取值0.4表示只绘制后面十分之六的内容,前面十分之四不予绘制。
android:trimPathEnd:指定几何路径到哪里结束绘制。取值为0.0到1.0,比如取值0.4表示只绘制前面十分之四的内容,后面十分之六不予绘制。
android:trimPathOffset:指定几何路径的绘制偏移。取值为0.0到1.0,表示线条从trimPathOffset+trimPathStart处一直绘制到trimPathOffset+trimPathEnd处。

路径信息有几个地方容易混淆,下面把相关细节详细说明一下:
1、关于butt和square的区别,乍看起来直线边缘与方形边缘没什么差别,但矢量图形的方形边缘其实是套上一个方形的帽子,既然是套上去,就会比没戴帽子的时候高一点,所以使用square的线条会比使用butt的线条要长一点。
2、关于butt和square的区别,miter保留了原样的尖角,而bevel会把尖角部分切掉一小块,看起来就变钝了。
3、trimPathOffset+trimPathEnd的和如果超过1,也会画出来。只是没有全部画出来,而是绘制从起点到trimPathOffset+trimPathEnd-1所处的位置。

可缩放矢量图形SVG标记

前面说到,path标签的android:pathData属性,取值需符合SVG标准。SVG全称为“Scalable Vector Graphics”,意即可缩放的矢量图形,它是一种图形格式,专门用于描述矢量图形的定义。

SVG标记比较抽象,下面先举个简单的例子,有了直观的概念更方便理解,如下所示:
        android:pathData="
            M 30,50
            L 75 35"
这个标记定义不难,首先“M 30,50”指的是把画笔移动到坐标点(30,50)的位置,后面的“L 75 35”指的是从当前位置画一根线段到坐标点(75,35)。说白了,就是在(30,50)和(75,35)两点之间画一根线段。

好了,每行定义一个动作,每行的第一个字符表示动作的类型,后面的数字表示动作经过的坐标点。这便是SVG标记的大概格式,万变不离其宗,掌握了规律学得更好更快。详细的SVG标记定义说明如下:
移动画笔 “M x0,y0” 把画笔移动到坐标点(x0,y0)。
画线段 “L x1 y1” 从当前位置(x0,y0)画一根线段到坐标点(x1,y1)。
画水平线段 “H x1” 从当前位置(x0,y0)画一根水平线到坐标点(x1,y0)。
画垂直线段 “V y1” 从当前位置(x0,y0)画一根垂直线到坐标点(x0,y1)。
画二次贝塞尔曲线 “Q xa ya x1 y1” 二次贝塞尔曲线的起点是当前位置,终点是(x1,y1),曲线中部向控制点(xa,ya)凸出。
画三次贝塞尔曲线 “C xa ya xb yb x1 y1” 三次贝塞尔曲线的起点是当前位置,终点是(x1,y1),曲线中部有两个控制点,分别向(xa,ya)和(xb,yb)两方向凸出。
画椭圆的圆弧 “A radius-x radius-y x-axis-rotation large-arc-flag sweep-flag x1 y1” 从当前位置拉出一段圆弧,圆弧的参数比较多,分别说明如下:
-- radius-x表示椭圆的横轴半径。
-- radius-y表示椭圆的纵轴半径。横轴半径等于纵轴半径时,表示这是个圆圈的圆弧。
-- x-axis-rotation表示圆弧的旋转角度。
-- large-arc-flag表示大弧标志,为0时表示取小弧度,1时取大弧度。
-- sweep-flag表示轨迹标志,为0表示逆时针方向,为1表示顺时针方向。
-- 圆弧经过某点,该点的横坐标为x1
-- 圆弧经过某点,该点的纵坐标为y1
闭合路径 “Z” 连接起点跟终点,即在起点(x0,y0)与终点之间画一根线段。

再来补充一下SVG标记的若干说明,如下所示:
1、每个命令都有大小写形式,大写代表后面的参数是绝对坐标,小写表示相对坐标。
2、参数之间用空格或逗号隔开,两种分隔符的效果是一样的。
3、关于圆弧的large-arc-flag和sweep-flag两个标志,光看文字说明其实不易理解,还是上个图观察观察:

下面使用SVG标记定义一个心形,先上个心形的效果图:

心形对应的矢量图形定义示例如下:

<vector xmlns:android="http://schemas.android.com/apk/res/android"android:width="256dp"android:height="256dp"android:viewportHeight="32"android:viewportWidth="32"><pathandroid:fillColor= "#ffaaaa"android:pathData= "M20.5,9.5c-1.955,0,-3.83,1.268,-4.5,3c-0.67,-1.732,-2.547,-3,-4.5,-3C8.957,9.5,7,11.432,7,14c0,3.53,3.793,6.257,9,11.5c5.207,-5.242,9,-7.97,9,-11.5C25,11.432,23.043,9.5,20.5,9.5z" />
</vector>

矢量动画AnimatedVectorDrawable

费了老大的劲搞清楚SVG标记,如果仅仅画个静态的矢量图形,未免大材小用了。其实矢量图形真正的意义在于矢量动画,通过动态计算几何路径的坐标,从而实现局部或整体的动画效果,这才是矢量图形的杀手锏呀。

Android提供了AnimatedVectorDrawable这么一个矢量动画类,但开发者还得通过属性动画及其xml标签方可实现动画定义。先看看AnimatedVectorDrawable的几个常用方法:
registerAnimationCallback : 注册动画监听器,需实现Animatable2.AnimationCallback接口的两个方法:onAnimationStart和onAnimationEnd。
start : 开始播放动画。
stop : 停止播放。
reverse : 倒过来播放。
再看看如何通过属性动画实现矢量动画效果。理论上,矢量图形的三个标签(vector、group、path)都有可以用来播放动画的属性;不过实际开发的时候,常用的只有三类属性可用作动画,说明如下:

变换类属性

这类属性包括vector标签的android:alpha,以及group标签的android:rotation、android:scaleX、android:scaleY、android:translateX、android:translateY等等,这几个属性分别对应于补间动画的灰度动画、旋转动画、缩放动画、平移动画。
因为该类属性实现的是大家熟悉的补间动画效果,所以这里就不再做演示了。

路径类属性

这类属性主要指path标签的android:pathData,通过设置几何路径的起始状态与终止状态,可实现两个几何形状之间的渐变效果,如一个圆圈从小变大,又如一条曲线变成直线等等。
下面是个从哭丧脸变为笑脸的动画截图:

下面是人脸的矢量图形定义文件vector_face_eye.xml:

<vector xmlns:android="http://schemas.android.com/apk/res/android"android:height="200dp"android:width="200dp"android:viewportHeight="100"android:viewportWidth="100" ><pathandroid:fillColor="@color/yellow"android:pathData="@string/path_circle"/><pathandroid:name="eye_left"android:strokeColor="@android:color/black"android:strokeWidth="4"android:strokeLineCap="round"android:pathData="@string/path_eye_left_sad"/><pathandroid:name="eye_right"android:strokeColor="@android:color/black"android:strokeWidth="4"android:strokeLineCap="round"android:pathData="@string/path_eye_right_sad"/><pathandroid:name="mouth"android:strokeColor="@android:color/black"android:strokeWidth="4"android:strokeLineCap="round"android:pathData="@string/path_face_mouth_sad"/>
</vector>

接着是脸部三处器官变化的属性动画定义文件。
下面是左眼的属性动画定义文件anim_smile_eye_left.xml:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"android:duration="3000"android:propertyName="pathData"android:valueFrom="@string/path_eye_left_sad"android:valueTo="@string/path_eye_left_happy"android:valueType="pathType"android:interpolator="@android:anim/accelerate_interpolator"/>

下面是右眼的属性动画定义文件anim_smile_eye_right.xml:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"android:duration="3000"android:propertyName="pathData"android:valueFrom="@string/path_eye_right_sad"android:valueTo="@string/path_eye_right_happy"android:valueType="pathType"android:interpolator="@android:anim/accelerate_interpolator"/>

下面是嘴巴的属性动画定义文件anim_smile_mouth.xml:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"android:duration="3000"android:propertyName="pathData"android:valueFrom="@string/path_face_mouth_sad"android:valueTo="@string/path_face_mouth_happy"android:valueType="pathType"android:interpolator="@android:anim/accelerate_interpolator"/>

最后是笑脸的矢量动画定义例子animated_vector_smile_eye.xml:

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"android:drawable="@drawable/vector_face_eye" ><targetandroid:name="mouth"android:animation="@anim/anim_smile_mouth" /><targetandroid:name="eye_left"android:animation="@anim/anim_smile_eye_left" /><targetandroid:name="eye_right"android:animation="@anim/anim_smile_eye_right" /></animated-vector>

不要忘了在代码中进行矢量动画的播放操作:

 private void startVectorSmile() {iv_vector_smile.setImageResource(R.drawable.animated_vector_smile_eye);Drawable drawable = iv_vector_smile.getDrawable();if (drawable instanceof AnimatedVectorDrawable) {((AnimatedVectorDrawable) drawable).start();}}

修剪类属性

这类属性包括path标签的android:trimPathStart和android:trimPathEnd,可实现矢量图形逐步展开或者逐步消失的动画效果。
下面是个支付宝支付成功的动画截图:

支付成功动画包含两个形状,首先在外面画个圆圈,然后在圆圈里面画个打勾符号。因为圆圈和打勾并不相连,如果按照一般的处理,就会一边画圆圈一边画打勾,这不是我们所希望的画完圆圈再画打勾的效果。所以要想让圆圈动画和打勾动画按顺序播放,得分别定义圆圈的矢量图形和打勾的矢量图形,然后等圆圈动画播放完毕,再开始播放打勾动画。

下面是圆圈的矢量图形定义文件vector_pay_circle.xml:

<vector xmlns:android="http://schemas.android.com/apk/res/android"android:height="100dp"android:viewportHeight="100"android:viewportWidth="100"android:width="100dp" ><pathandroid:name="circle"android:pathData="M 10,50A 40 40 0 1 0 10 49"android:strokeAlpha="1"android:strokeColor="@color/blue_sky"android:strokeLineCap="round"android:strokeWidth="3" /></vector>

下面是打勾的矢量图形(含圆圈图形)定义文件vector_pay_success.xml:

<vector xmlns:android="http://schemas.android.com/apk/res/android"android:height="100dp"android:viewportHeight="100"android:viewportWidth="100"android:width="100dp" ><pathandroid:name="circle"android:pathData="M 10,50A 40 40 0 1 0 10 49"android:strokeAlpha="1"android:strokeColor="@color/blue_sky"android:strokeLineCap="round"android:strokeWidth="3" /><pathandroid:name="hook"android:pathData="M 30,50L 45 65L 75 35"android:strokeAlpha="1"android:strokeColor="@color/blue_sky"android:strokeLineCap="butt"android:strokeWidth="3" /></vector>

接着是支付成功的属性动画的xml定义文件anim_pay.xml:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"android:duration="1000"android:interpolator="@android:interpolator/linear"android:propertyName="trimPathEnd"android:valueFrom="0"android:valueTo="1"android:valueType="floatType" />

最后是矢量动画的定义文件,下面这个用来播放圆圈动画:

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"android:drawable="@drawable/vector_pay_circle"><targetandroid:name="circle"android:animation="@anim/anim_pay" /></animated-vector>

下面这个用来播放圆圈动画后继的打勾动画:

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"android:drawable="@drawable/vector_pay_success"><targetandroid:name="hook"android:animation="@anim/anim_pay" /></animated-vector>

圆圈动画播放完毕,接着播放打勾动画,这要在代码中控制,具体的是调用AnimatedVectorDrawable对象的registerAnimationCallback方法,一旦监听到原动画播放结束,然后开始播放新动画。

点击下载本文用到的矢量图形与矢量动画的工程代码

点此查看Android开发笔记的完整目录

Android开发笔记(一百三十二)矢量图形与矢量动画相关推荐

  1. Android开发笔记(三十二)文件基础操作

    File类 File类是java中的文件操作工具类,它的常用方法如下: File构造函数 : 根据文件路径构造File对象 delete : 删除文件 exists : 判断文件是否存在 getNam ...

  2. Android开发笔记(六十二)HTTP数据格式的解析

    json解析 android有两种主流的json解析方案,一种是sdk自带的由Google提供的json(包名前缀为org.json),另一种是Alibaba提供的第三方jar包fastjson(包名 ...

  3. Android开发笔记(五十二)通知推送Notification

    PendingIntent 准备工作复习一下PendingIntent,前面的博文< Android开发笔记(五十)定时器AlarmManager>已经提到了它.PendingIntent ...

  4. Android开发笔记(四十二)Broadcast的生命周期

    Broadcast是什么 广播的特性 广播(Broadcast)用于Android组件之间的灵活通信,它与Activity和Service的区别在于: 1.Activity和Service都只能一对一 ...

  5. Android开发笔记(三十六)展示类控件

    View/ViewGroup View是单个视图,所有的控件类都是从它派生出来:而ViewGroup是个视图组织,所有的布局视图类都是从它派生出来.由于View和ViewGroup是基类,因此很少会直 ...

  6. Android开发笔记(三十五)页面布局视图

    布局视图的类别 布局视图有五类,分别是线性布局LinearLayout.相对布局RelativeLayout.框架布局FrameLayout.绝对布局AbsoluteLayout.表格布局TableL ...

  7. Android开发笔记(八十二)SDK版本兼容

    统一主题与风格 Android控件很多属性都有默认值,比如文字默认黑色.编辑框默认透明背景等等,但因为android是开源的,各厂商都会自行修修补补,所以很多时候默认值并不靠谱.举例如下: 1.在某些 ...

  8. Android开发笔记(七十二)数据加密算法

    编码算法 URL编码 URL编码其实并非加解密算法,只是对特殊字符进行字符转义,从而方便在URL中传输参数.URL编码有两种方式,一种是狭义的URL编码,另一种是广义的URL编码. 狭义的URL编码指 ...

  9. Android开发笔记(三十八)列表类视图

    AdapterView AdapterView顾名思义是适配器视图,Spinner.ListView和GridView都间接继承自AdapterView,这三个视图都存在多个元素并排展示的情况,所以需 ...

  10. Android开发笔记(三十九)Activity的生命周期

    与生命周期有关的方法 下面是Activity类与生命周期有关的方法: onCreate : 创建页面 onStart : 开始页面 onStop : 停止页面 onResume : 恢复页面 onPa ...

最新文章

  1. MapReduce编程实战之“初识”
  2. SBO中流程控制功能的实现-SBO_SP_TransactionNotification
  3. php两个数组找公共部分,PHP开发中如何查找两个数组的交集
  4. html 子级选择器,css3子选择器(选择某个标签做内容)
  5. 使用will和would来提出请求_42
  6. 如何在Vue中使用Font Awesome?
  7. 高瓴突然清仓阿里巴巴、蔚来、理想、小鹏,是怎么回事?
  8. 2.1 LibCurl编程流程(转)
  9. 45.Linux/Unix 系统编程手册(下) -- System V IPC 介绍
  10. python网校_《猎豹网校:快速掌握Python系统管理-53讲》
  11. [篇五章二]_使用 USB 系统安装盘在真机上安装激活 Windows 10 LTSC 2021 中文企业版系统
  12. H264/H265 PS 流分析
  13. 记住网站建设这几个步骤
  14. 陶哲轩论文解析:从特征值到特征向量
  15. JPBC部分API说明
  16. 怎么在CAD三维图面上添加一长段说明文字?
  17. 手机微信内置浏览器清理缓存
  18. 微信小程序抓包https抓包的血泪史
  19. 小技巧---网线线序,586B网线:橙白 橙,绿白 蓝,蓝白 绿,棕白 棕
  20. 单臂路由的原理以及配置

热门文章

  1. 吴恩达机器学习 逻辑回归 作业3(手写数字分类) Python实现 代码详细解释
  2. python和anaconda区别_Pycharm、Anaconda到底是什么?有什么区别?
  3. oracle20g,GaussDB 100 OLTP: 缩减创建数据库的20G空间需求及GS-00714
  4. python可以神奇的做什么_可以用 Python 编程语言做哪些神奇好玩的事情?
  5. 富文本编辑器CKEditor 5开发环境搭建
  6. 换行与回车(\r \n)的起源以及在编制语言中的使用
  7. 第2章[2.4] Ext JS的类与类体系
  8. 合工大计算机在职研究生好考吗,合肥工业大学在职研究生2019年考研必知
  9. java oracle数据备份_Java后台备份oracle数据库脚本
  10. git 怎么备份本地分支_同步管理本地git仓库和github仓库上的分支