首页 > 代码库 > Android开发笔记(一百三十二)矢量图形与矢量动画
Android开发笔记(一百三十二)矢量图形与矢量动画
矢量图形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=http://www.mamicode.com/"
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"> <path android:fillColor= "#ffaaaa" android:pathData= http://www.mamicode.com/"M20.5,9.5>矢量动画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" > <path android:fillColor="@color/yellow" android:pathData=http://www.mamicode.com/"@string/path_circle"/>>
接着是脸部三处器官变化的属性动画定义文件。
下面是左眼的属性动画定义文件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" > <target android:name="mouth" android:animation="@anim/anim_smile_mouth" /> <target android:name="eye_left" android:animation="@anim/anim_smile_eye_left" /> <target android: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" > <path android:name="circle" android:pathData=http://www.mamicode.com/">下面是打勾的矢量图形(含圆圈图形)定义文件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" > <path android:name="circle" android:pathData=http://www.mamicode.com/">
接着是支付成功的属性动画的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"> <target android: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"> <target android:name="hook" android:animation="@anim/anim_pay" /> </animated-vector>
圆圈动画播放完毕,接着播放打勾动画,这要在代码中控制,具体的是调用AnimatedVectorDrawable对象的registerAnimationCallback方法,一旦监听到原动画播放结束,然后开始播放新动画。
点击下载本文用到的矢量图形与矢量动画的工程代码
点此查看Android开发笔记的完整目录Android开发笔记(一百三十二)矢量图形与矢量动画
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。