【Android】动画
本章介绍App开发常见的动画显示技术,主要包括如何使用帧动画实现电影播放效果、如何使用补间动画完成视图的4种基本状态变化、如何使用属性动画达成视图各种状态的动态变换效果、如何使用矢量动画展现更加精细的局部炫动特效,以及动画技术常用的3种代表手段。
帧动画
本节介绍帧动画相关的技术实现,首先说明如何通过动画图形与宿主视图播放帧动画,接着阐述播放GIF动画存在的问题和对应的解决思路与技术方案,最后介绍如何使用过渡图形实现两幅图片之间的淡入、淡出动画。
帧动画的实现
Android的动画分为3大类:帧动画、补间动画和属性动画。其中,帧动画是实现原理最简单的一种,跟现实生活中的电影胶卷类似,都是在短时间内连续播放多张图片,从而模拟动态画面的效果。
具体到代码实现,帧动画由动画图形AnimationDrawable生成。下面是AnimationDrawable的常用方法。
- addFrame:添加一幅图片帧,并指定该帧的持续时间(单位毫秒)。
- setOneShot:设置是否只播放一次。为true表示只播放一次,为false表示循环播放。
- start:开始播放。注意,设置宿主视图后才能进行播放。
- stop:停止播放。
- isRunning:判断是否正在播放。
显示GIF动画
GIF在Windows上是常见的图片格式,主要用来播放短小的动画。Android虽然号称支持PNG、JPG、GIF三种图片格式,但是并不支持直接播放GIF动图,如果在图像视图中加载一张GIF文件,只会显示GIF文件的第一帧图片。
要想在手机上显示GIF文件,就要借助于帧动画技术,具体的实现方式主要有以下两种:
(1)开发者在电脑上把GIF文件手工分解为一组帧图片,放入工程的资源目录中,再通过动画图形显示帧动画。
(2)在代码中将GIF文件自动分解为一系列图片数据,并获取每帧的持续时间,然后通过动画图形动态加载帧图片。该方式适合播放从服务器获取的GIF文件。
上述两种显示GIF动图的方法显然都不简单,毕竟GIF文件还是很流行的动图格式,因而Android从9.0开始增加了新的图像解码器ImageDecoder,该解码器支持直接读取GIF文件的图形数据,通过搭配具备动画特征的图形工具Animatable,即可轻松实现在App中播放GIF动图。
淡入淡出动画
帧动画的帧显示方式采用后面一帧直接覆盖前面一帧,这在快速轮播时没什么问题,但是如果每帧的间隔时间比较长(比如超过0.5秒),两帧之间的画面切换就会很生硬,直接从前一帧变成后一帧会让人觉得很突兀。为了解决这种长间隔切换图片在视觉效果方面的问题,Android提供了过渡图形TransitionDrawable处理两张图片之间的渐变显示,即淡入淡出的动画效果。
过渡图形同样需要宿主视图显示该图形,即调用ImageView对象的setImageDrawable方法进行图形加载操作。下面是TransitionDrawable的常用方法说明。
- 构造函数:指定过渡图形的图形数组。该图形数组大小为2,包含前后两张图形。
- startTransition:开始过渡操作。这里需要先设置宿主视图,然后才能进行渐变显示。
- resetTransition:重置过渡操作。
- reverseTransition:倒过来执行过渡操作。
补间动画(视图动画)
本节介绍补间动画的原理与用法,首先指出补间动画有4大类,分别是灰度动画、平移动画、缩放动画和旋转动画,介绍这4种动画的基本用法;接着阐述补间动画的原理,基于旋转动画的思想实现摇摆动画;然后介绍如何使用集合动画同时展示多种动画效果。
补间动画的种类
一张图形内部能否运用渐变效果?比如对图片的大小进行自动缩放等。正好,Android提供了补间动画,允许开发者实现某个视图的动态变换,具体包括4类动画效果,分别是灰度动画、平移动画、缩放动画和旋转动画。为什么把这4种动画称为补间动画呢?因为由开发者提供动画的起始状态值与终止状态值,然后系统按照时间推移计算中间的状态值,并自动把中间状态的视图补充到起止视图中,自动补充中间视图的动画就被简称为“补间动画”。
补间动画的4类动画(灰度动画AlphaAnimation、平移动画TranslateAnimation、缩放动画ScaleAnimation和旋转动画RotateAnimation)都来自于共同的动画类Animation,因此同时拥有Animation的属性与方法。下面是Animation的常用方法说明。
- setFillAfter:设置是否维持结束画面。true表示动画结束后停留在结束画面,false表示动画结束后恢复到开始画面。
- setRepeatMode:设置重播模式。Animation.RESTART表示从头开始,Animation.REVERSE表示倒过来开始。默认为Animation.RESTART。
- setRepeatCount:设置重播次数。默认为0表示只播放一次。
- setDuration:设置动画的持续时间。单位毫秒。
- setInterpolator:设置动画的插值器。
- setAnimationListener:设置动画事件的监听器。需实现接口AnimationListener的3个方法。
- onAnimationStart:在动画开始时触发。
- onAnimationEnd:在动画结束时触发。
- onAnimationRepeat:在动画重播时触发。
与帧动画一样,补间动画也需要找一个宿主视图,对宿主视图施展动画效果。不同的是,帧动画的宿主视图只能是ImageView相关的图像视图,而补间动画的宿主视图可以是任意视图,只要派生自View类就行。给补间动画指定宿主视图的方式很简单,调用宿主对象的startAnimation方法即可命令宿主视图开始动画,调用宿主对象的clearAnimation方法即可要求宿主视图清除动画。
具体到每种补间动画又有不同的初始化方式。下面来看具体说明。
(1)初始化灰度动画:在构造函数中指定视图透明度的前后数值。取值为0.0~1.0,0表示完全不透明,1表示完全透明。
(2)初始化平移动画:在构造函数中指定视图左上角在平移前后的坐标值。其中,第一个参数为平移前的横坐标,第二个参数为平移后的横坐标,第三个参数为平移前的纵坐标,第四个参数为平移后的纵坐标。
(3)初始化缩放动画:在构造函数中指定视图横纵坐标的前后缩放比例。缩放比例取值0.5表示缩小到原来的二分之一,取值2表示放大到原来的两倍。其中,第一个参数为缩放前的横坐标比例,第二个参数为缩放后的横坐标比例,第三个参数为缩放前的纵坐标比例,第四个参数为缩放后的纵坐标比例。
(4)初始化旋转动画:在构造函数中指定视图的旋转角度。其中,第一个参数为旋转前的角度,第二个参数为旋转后的角度,第三个参数为圆心的横坐标类型,第四个参数为圆心横坐标的数值比例,第五个参数为圆心的纵坐标类型,第六个参数为圆心纵坐标的数值比例。坐标类型的取值说明见表12-1。
补间动画的原理
与动画播放有关的代码只有applyTransformation方法。该方法很简单,提供了两个输入参数,第一个参数为插值时间,即逝去的时间所占的百分比,第二个参数为转换器。方法内部根据插值时间计算当前所处的角度degrees,最后使用转换器把视图旋转到该角度。
查看其他补间动画的源码,发现都与RotateAnimation的处理大同小异,对中间状态的视图变换处理不外乎以下两个步骤:
步骤01 根据插值时间计算当前的状态值(如灰度、距离、比率、角度等)。
步骤02 在宿主视图上使用该状态值进行变换操作。
在飞掠横幅中使用补间动画
第11章介绍飞掠视图ViewFlipper时,结合手势检测器GestureDetector实现了飞掠横幅的效果。不过前后Banner的飞掠切换有些生硬,后面的广告图一下子把前面的广告图覆盖,显得十分突兀,完全不如ViewPager那样翻页自然。现在我们正好活学活用,试试利用补间动画技术给飞掠横幅加上动画翻页变换,看看能否达到自然翻页的预期效果。
第11章提到,ViewFlipper有以下4个操作动画的方法:
- setInAnimation:设置视图的移入动画。
- getInAnimation:获取移入动画的动画对象。
- setOutAnimation:设置视图的移出动画。
- getOutAnimation:获取移出动画的动画对象。
通过这4个动画方法加载动画定义,应该能实现飞掠视图前后切换的动画效果。
首先定义几个动画定义文件,用来描述移入动画和移出动画的行为。具体地说,包括4个动画定义文件:向左移入动画、向左移出动画、向右移入动画和向右移出动画。下面对这4个动画定义文件分别进行说明。
(1)向左移入动画,用来描述Banner向左翻页时右边页面的移入行为,动画文件名为push_left_in.xml。
(2)向左移出动画,用来描述Banner向左翻页时左边页面的移出行为,动画文件名为push_left_out.xml。
(3)向右移入动画,用来描述Banner向右翻页时左边页面的移入行为,动画文件名为push_right_in.xml。
(4)向右移出动画,用来描述Banner向右翻页时右边页面的移出行为,动画文件名为push_right_out.xml。
属性动画
本节介绍属性动画的应用场合与进阶用法,首先说明为何属性动画是补间动画的升级版,以及属性动画的基本用法;接着说明如何运用属性动画组合实现多个属性动画的同时播放与顺序播放效果;最后对动画技术中的插值器和估值器进行分析,并演示不同插值器的动画效果。
属性动画的用法
视图View有许多状态属性,4种补间动画只对其中6种属性进行操作,这6种属性的说明见表12-2。
可是每个控件的属性远不止这6种,如果要求对视图的背景颜色做渐变处理,补间动画就无能为力了。为此,Android自3.0后引入了属性动画ObjectAnimator,属性动画突破了补间动画的局限,允许视图的所有属性都能实现渐变的动画效果,例如背景颜色、文字颜色、文字大小等。只要设定某属性的起始值与终止值、渐变的持续时间,属性动画即可实现该属性的动画渐变效果。
下面是ObjectAnimator的常用方法。
- ofInt:定义整型属性的属性动画。
- ofFloat:定义浮点型属性的属性动画。
- ofArgb:定义颜色属性的属性动画。
- ofObject:定义对象属性的属性动画。用于不是上述三种类型的属性,例如Rect对象。
以上4个of方法的第一个参数为宿主视图对象,第二个参数为需要变化的属性名称,第三个参数后为属性变化的各个状态值。注意,of方法后面的参数个数是变化的。如果第3个参数是状态A,第4个参数是状态B,属性动画就从A状态变为B状态;如果第3个参数是状态A,第4个参数是状态B,第5个参数是状态C,属性动画就先从A状态变为B状态,再从B状态变为C状态。
- setRepeatMode:设置重播模式。ValueAnimator.RESTART表示从头开始,ValueAnimator.REVERSE表示倒过来开始。默认为ValueAnimator.RESTART。
- setRepeatCount:设置重播次数。默认为0表示只播放一次。
- setDuration:设置动画的持续时间。单位毫秒。
- setInterpolator:设置动画的插值器。
- setEvaluator:设置动画的估值器。
- start:开始播放动画。
- cancel:取消播放动画。
- end:结束播放动画。
- pause:暂停播放动画。
- resume:恢复播放动画。
- reverse:倒过来播放动画。
- isRunning:判断动画是否在播放。注意,暂停时,isRunning方法仍然返回true。
- isPaused:判断动画是否被暂停。
- isStarted:判断动画是否已经开始。注意,曾经播放与正在播放都算已经开始。
- addListener:添加动画监听器,需实现接口AnimatorListener的4个方法。
- onAnimationStart:在动画开始播放时触发。
- onAnimationEnd:在动画结束播放时触发。
- onAnimationCancel:在动画取消播放时触发。
- onAnimationRepeat:在动画重播时触发。
- removeListener:移出指定的动画监听器。
- removeAllListeners:移出所有动画监听器。
插值器和估值器
前面在介绍补间动画与属性动画时提到了插值器,属性动画还提到了估值器,因为插值器和估值器是相互关联的,所以放到一起介绍。
插值器用来控制属性值的变化速率,也可以理解为动画播放的速度,默认是匀速播放。要给动画播放指定某种速率形式,调用setInterpolator方法设置对应的插值器实现类即可,无论是补间动画、集合动画、属性动画,还是属性动画组合,都可以设置插值器。插值器实现类的说明见表12-3。
估值器专用于属性动画,主要描述该属性的数值变化要采用什么单位,比如整型数的渐变数值要取整,颜色的渐变数值为ARGB格式的颜色对象,矩形的渐变数值为Rect对象等。要给属性动画设置估值器,调用属性动画对象的setEvaluator方法即可。估值器实现类的说明见表12-4。
表12-4 估值器实现类的说明
一般情况下,无须单独设置属性动画的估值器,使用系统默认的估值器即可。但是如果属性类型不是int、float、argb三种,只能通过ofObject方法构造属性动画对象,就必须指定该属性的估值器,否则系统不知道如何计算渐变属性值。为方便记忆属性动画的构造方法与估值器的关联关系,表12-5列出了两者之间的对应关系。
矢量动画
本节介绍了矢量动画的基础知识与实现过程,首先描述了矢量图形的XML文件格式,然后详细解释了可缩放矢量图形SVG标记的标准定义,接着阐述了如何利用属性动画的手段来实现矢量动画,最后演示了如何通过矢量技术仿照支付宝的支付成功动画。
矢量图形
矢量是一种既有大小又有方向的几何对象,它通常被标示为一个带箭头的线段。若干个矢量拼接在一起,便形成了矢量图形。矢量图不同于一般的图形,它是由一系列几何曲线构成的图像,这些曲线又以数学上定义的坐标点连接而成。
安卓的矢量图形由XML文件定义,故而需要开发者在drawable目录提供一个XML格式的矢量图形定义,然后系统根据矢量定义自动计算该图形的绘制区域。因为绘图结果是动态计算得到,所以不管缩放到多少比例,矢量图形都会一样的清晰,不像普通位图那样拉大后会变模糊。矢量图形的XML文件结构可分为三个层次:根标签、组标签、路径标签,分别介绍如下。
1. 根标签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单位的绝对数值;而后者指的是矢量图形为内部几何路径所参照的空间范围,故而采用了不带单位的相对数值。正因为矢量图形中的几何路径以相对坐标来标记,所以不管矢量图形缩放到多少比例,其内部的几何形状也会按同样比例缩放。
2. 组标签group
group标签定义了一组路径的共同行为(如一起旋转、一起缩放、一起平移等)。该标签支持的主要属性说明如下。
- android:name:指定分组对象的名称。
- android:pivotX:指定旋转中心点的横轴坐标。
- android:pivotY:指定旋转中心点的纵轴坐标。
- android:rotation:指定分组对象的旋转角度。
- android:scaleX:指定分组对象在横轴上的缩放比例。取值0.5表示缩小一半,取值2.0表示放大一倍。
- android:scaleY:指定分组对象在纵轴上的缩放比例。
- android:translateX:指定分组对象在横轴上的平移距离。
- android:translateY:指定分组对象在纵轴上的平移距离。
3. 路径标签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处。
可缩放矢量图形SVG标记
上一小节说到,path标签的android:pathData属性,取值需符合SVG标准。SVG全称为“Scalable Vector Graphics”,意即可缩放的矢量图形,它是一种图形格式,专门用于描述矢量图形的定义。SVG标记比较抽象,下面先举个简单的例子,有了直观的概念更方便理解,如下所示:
这个标记的定义不难理解,首先“M 30,50”指的是把画笔移动到坐标点(30,50)的位置,字母M代表move;后面的“L 75 35”指的是从当前位置画一根线段到坐标点(75,35),字母L代表line。说白了,就是在(30,50)和(75,35)两点之间画一根线段。
SVG数据的每行定义一个动作,每行的第一个字符表示动作的类型,后面的数字表示动作经过的坐标点。这便是SVG标记的基本格式。
另外补充介绍一下SVG标记的几个要点:
(1)每个命令都有大小写形式,大写表示后面的参数是绝对坐标,小写表示相对坐标。
(2)参数之间用空格或逗号隔开,两种分隔符的效果是一样的。
(3)画椭圆圆弧的时候,用到了较多的参数,分别说明如下。
radius-x和radius-y:表示椭圆的横轴半径和纵轴半径。
x-axis-rotation:表示圆弧的旋转角度。
large-arc-flag:表示大弧标志,为0时表示取小弧度,1时取大弧度。
sweep-flag:表示轨迹标志,为0表示逆时针方向,为1表示顺时针方向。
x1和y1:表示圆弧经过某点,该点的横坐标为x1,纵坐标为y1。
利用属性动画实现矢量动画
费了老大的劲搞清楚SVG标记,如果仅仅画个静态的矢量图形,未免大材小用了。其实矢量图形真正的意义在于矢量动画,通过动态计算几何路径的坐标,从而实现局部或整体的动画效果,这才是矢量图形的杀手锏呀。
Android提供了AnimatedVectorDrawable这么一个矢量动画类,但开发者还得通过属性动画及其XML标签方可实现动画定义。先看看AnimatedVectorDrawable的以下几个常用方法:
- registerAnimationCallback:注册动画监听器,需实现Animatable2.AnimationCallback接口的两个方法,即onAnimationStart和onAnimationEnd。
- start:开始播放动画。
- stop:停止播放。
- reverse:倒过来播放。
再看看如何通过属性动画实现矢量动画效果。理论上,矢量图形的三个标签(vector、group、path)都拥有可以用来播放动画的属性;不过实际开发的时候,常用的只有三类属性可用作动画,详述如下。
1. 变换类属性
该类属性包括vector标签的android:alpha,以及group标签的android:rotation、android:scaleX、android:scaleY、android:translateX、android:translateY等等,这几个属性分别对应于补间动画的灰度动画、旋转动画、缩放动画、平移动画。
因为该类属性实现的是大家熟悉的补间动画效果,所以这里就不再做具体演示了。
2. 路径类属性
该类属性主要指path标签的android:pathData,通过设置几何路径的起始状态与终止状态,可实现两个几何形状之间的渐变效果,如一个圆圈从小变大,又如一条曲线变成直线等。
3. 修剪类属性
该类属性包括path标签的android:trimPathStart和android:trimPathEnd,可实现矢量图形逐步展开或者逐步消失的动画效果。
动画的实现手段
本节介绍动画技术常见的3种实现手段,包括以帧动画为代表的延时重绘方式、以补间动画和属性动画为代表的设置状态参数方式以及为解决拖曳卡顿问题而采用的滚动器。
使用延时重绘
延时重绘是最基本的动画实现手段,代表技术为帧动画,每隔若干毫秒就用新图片换掉原图片,人眼看过去仿佛画面动起来了。
当然,除了帧动画,还有不少地方采用延时重绘技术,比如第6章的圆弧进度动画、第7章的Banner指示器等,它们都是连续调用onDraw或dispatchDraw方法实现动画效果。
设置状态参数
设置状态参数是最常见的动画实现手段,代表技术为补间动画和属性动画,通过持续改变视图的状态属性数值让该视图蹦起来、跳起来。
虽然通过属性动画可实现大多数状态变更动画,但是属性动画要求有明确的初始状态值和结束状态值,如果这些起止状态值无法确定,中间还要加入其他运算,属性动画就无法胜任如此复杂的要求,只能自己实现状态变更动画了。
滚动器Scroller
第11章的实战项目通过移动手势拖曳图片到指定位置,拖曳后直接在新位置重绘整个图片,不知道读者有没有发现,这种拖曳方式的画面存在卡顿现象。因为根据人眼的机理,每秒连续播放20帧图片才不易感觉到画面卡顿,而拖曳重绘的做法频率绝对小于每秒20次,所以自然会出现画面卡顿。
为解决拖曳卡顿的问题,Android提供了滚动器Scroller,通过Scroller可以实现平滑滚动的效果。下面是Scroller的常用方法说明。
- startScroll:设置开始滑动的参数,包括起始的横纵坐标、横纵偏移量和滑动的持续时间。
- computeScrollOffset:计算滑动偏移量。返回值可判断滑动是否结束,返回fasle表示滑动结束,返回true表示还在滑动中。
- getCurrX:获得当前的横坐标。
- getCurrY:获得当前的纵坐标。
- getDuration:获得滑动的持续时间。
- forceFinished:强行停止滑动。
- isFinished:判断滑动是否结束。返回fasle表示还未结束,返回true表示滑动结束。
原文地址:https://blog.csdn.net/weixin_43902408/article/details/142700706
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!