androidanimatorset 循环动画属性动画结束后状态怎样变回原状态

Android Animation运行原理详解 - 简书
<div class="fixed-btn note-fixed-download" data-toggle="popover" data-placement="left" data-html="true" data-trigger="hover" data-content=''>
写了29982字,被47人关注,获得了90个喜欢
Android Animation运行原理详解
作为Android程序员,或者是想要去模仿一些酷炫的效果,或者是为了实现视觉的变态需求,或者是压抑不住内心的创造欲想要炫技,我们不可避免地需要做各种动画。Android中,动画主要分为帧动画、插间动画以及属性动画。帧动画最为简单,是用一系列的素材作为关键帧逐帧播放,常用于制作加载动画,其工作量主要在设计部分;插间动画与属性动画则更多地是需要开发通过控制各种动画参数来实现,只有系统地理解Android中动画运行的原理,才能创作出更出色的动画,属性动画在下一篇文章中分析,本文主要分享我在探索插间动画运行原理过程中的一些收获,包括:Matrix如何控制动画参数;动画中各参数具体起什么作用;透明度动画、缩放动画、平移动画以及旋转动画的运行逻辑;动画在View的绘制过程中如何被应用。
2. Matrix介绍
在Android中,Matrix是一个3 x 3的矩阵:
Matrix 3 x 3 矩阵
Matrix可将一个点映射到另一个点,矩阵中包含了处理缩放、透视以及平移的区域,从而可用于控制实现平移、缩放、旋转等动画效果。强烈建议阅读以更深入地了解Matrix实现动画控制原理,这里仅摘录其中的关键信息:
结论一:设对给定的图像依次进行了基本变化F1、F2、F3…..、Fn,它们的变化矩阵分别为T1、T2、T3…..、Tn,图像复合变化的矩阵T可以表示为:T = TnTn-1…T1。
结论二:Preconcats matrix相当于右乘矩阵,Postconcats
matrix相当于左乘矩阵。
Matrix还给我们提供了各种友好的接口来组合生成复杂的动画,举个例子:假如我们想要实现一个平移(a,b)之后旋转(c,d)的动画,那用Matrix的实现代码就是这样的:
Matrix matrix = new Matrix();
matrix.setTranslate(a, b);
matrix.postScale(c, d);
3. Animation运行原理分析
(1)基本属性介绍
使用过Animation的同学对下述基本属性应该非常熟悉,这里为了文章完整性,特地赘述一下:
mStartTime:动画实际开始时间
mStartOffset:动画延迟时间
mFillEnabled:mFillBefore及mFillAfter是否使能
mFillBefore:动画结束之后是否需要进行应用动画
mFillAfter:动画开始之前是否需要进行应用动画
mDuration:单次动画运行时长
mRepeatMode:动画重复模式(RESTART、REVERSE)
mRepeatCount:动画重复次数(INFINITE,直接值)
mInterceptor:动画插间器
mListener:动画开始、结束、重复回调监听器
虽然大部分都知道上面这些属性怎么用,但是可能还是有一些人对这些字段为什么有这样的作用不甚明白,于是我们就来分析一下。
(2)计算动画数据
Animation在其getTransformation函数被调用时会计算一帧动画数据,而上面这些属性基本都是在计算动画数据时发光发热,我们先看看getTransformation函数的运行逻辑:
若startTime为START_ON_FIRST_FRAME(值为-1)时,将startTime设定为curTime
计算当前动画进度: normalizedTime = (curTime - (startTime + startOffset))/duration
若mFillEnabled==false:将normalisedTime夹逼至[0.0f, 1.0f]
判断是否需要计算动画数据:
若normalisedTime在[0.0f, 1.0f],需计算动画数据
若normalisedTime不在[0.0f, 1.0f]:
normalisedTime&0.0f, 仅当mFillBefore==true时才计算动画数据
normalisedTime&1.0f, 仅当mFillAfter==true时才计算动画数据
若需需要计算动画数据:
若当前为第一帧动画,触发mListener.onAnimationStart
若mFillEnabled==false:将normalisedTime夹逼至[0.0f, 1.0f]
根据插间器mInterpolator调整动画进度:
interpolatedTime = mInterpolator.getInterpolation(normalizedTime)
若动画反转标志位mCycleFlip为true,则
interpolatedTime = 1.0 - normalizedTime
调用动画更新函数applyTransformation(interpolatedTime, transformation)计算出动画数据
若夹逼之前normalisedTime大于1.0f, 则判断是否需继续执行动画:
已执行次数mRepeatCount等于需执行次数mRepeated
若未触发mListener.onAnimationEnd,则触发之
已执行次数mRepeatCount不等于需执行次数mRepeated
自增mRepeatCount
重置mStartTime为-1
若mRepeatMode为REVERSE,则取反mCycleFlip
触发mListener.onAnimationRepeat
这一段是根据getTransformation源码分析出来的,建议有兴趣的同学可以直接查看源码。上面这段分析留了一个不小的悬念,那就是动画更新函数是什么鬼,这个函数在Animation这个抽象类中仅仅是个钩子函数,由其子类提供具体实现,于是自然而然地引出了我们的下一个主题:主流动画介绍。
(3)主流动画分析
AlphaAnimation:透明度动画
mFromAlpha:起始透明度
mToAlpha:终止透明度
applyTransformation函数实现
transformation.setAlpha(mFromAlpha + ((mToAlpha - mFromAlpha) * interpolatedTime))
ScaleAnimation:缩放动画
mFromX:起始X值
mToX:终止X值
mFromY:起始Y值
mToY:终止Y值
mPivotX:缩放中心点X坐标
mPivotY:缩放中心点Y坐标
属性计算逻辑
mFromX、mToX、mFromY、mToY计算
Float类型scale直接值
Faction类型相对值
相对于自身(%):百分比转换为float直接值
相对于父亲(%p):根据父亲size计算出size直接值,然后计算与本身size的百分比,最后转换为float直接值
Dimension类型size直接值:计算与本身size的百分比,然后转换为float直接值
mPivotX、mPivotY计算
ABSOLUTE类型直接值
RELATIVE_TO_SELF类型相对值:相对值乘以自身size得到直接值
RELATIVE_TO_PARENT类型相对值:相对值乘以父亲size得到直接值
applyTransformation函数实现
sx = mFromX + ((mToX - mFromX) * interpolatedTime)
sy = mFromY + ((mToY - mFromY) * interpolatedTime)
是否设定缩放中心点:
若mPivotX==0 且 mPivotY==0:transformation.getMatrix().setScale(sx, sy)
否则:transformation.getMatrix().setScale(sx, sy, mPivotX, mPivotY)
TranslateAnimation:平移动画
mFromXDelta
mFromYDelta
属性计算逻辑
同ScaleAnimation中mPivotX、mPivotY的计算逻辑
applyTransformation函数实现
dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime)
dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime)
transformation.getMatrix().setTranslate(dx, dy)
RotateAnimation:旋转动画
mFromDegrees
mToDegrees
属性计算逻辑
mFromDegrees、mToDegrees均为角度(°)绝对值
mPivotX、mPivotY计算逻辑同ScaleAnimation
applyTransformation函数实现
是否设定缩放中心点:
若mPivotX==0 且 mPivotY==0:transformation.getMatrix().setScale(sx, sy)
否则:transformation.getMatrix().setScale(sx, sy, mPivotX, mPivotY)
透明度、缩放、平移以及旋转是最基本的动画,通过组合这些动画可以实现各种不一样的酷炫的效果,但是怎么才能实现这些动画的组合,这就不得不提到AnimationSet了。
(4) AnimationSet分析
AnimationSet是动画集合,用于组合运行多个动画,仅支持playTogether模式。
AnimationSet继承了Animation的字段,但是字段的应用有一些变化:
duration, repeatMode, fillBefore, fillAfter:这些属性会传递应用到所有的子Animation
repeatCount, fillEnabled:这些属性在AnimationSet中不被应用
startOffset, shareInterpolator:这些属性仅用于AnimationSet,不会传递至子Animation
4.0以前在xml中设置duration, repeatMode, fillBefore, fillAfter, startOffset不会被应用,但是4.0之后再xml中设定这些属性跟运行时设定效果一致
一些值的计算逻辑:
duration:
缺省时,取所有子Animation中最长的duration;
已设定时,返回mDuration
hasAlpha、willChangeTransformationMatrix、willChangeBounds:当有子Animation时,所有子Animation的值取“或”
startTime:取所有子Animation中最小的startTime
子Animation中startOffset处理:
保存子Animation的原始startOffset
设置子Animation的startOffset为原始startOffset与AnimationSet的startOffset之和
保存的原始startOffset在AnimationSet.clear是用于恢复各子Animation的startOffset
applyTransformation函数实现
顺序调用子Animation的applyTransformation,然后利用pose组合所有子Animation返回的Transformation作为该AnimationSet当前帧的变换状态
started及more值取所有子Animation对应值的“或”
ended值取所有子Animation对应值的“与”
当started第一次为true时,调用AnimationSet的mListener.onAnimationStart
当ended第一次为true(此时所有子Animation均结束)时,调用AnimationSet的mListener.onAnimationEnd
介绍完了主流动画以及组合动画,是不是Animation就介绍完了?其实不然,里面还漏掉了一个重要角色,那就是计算得到的动画数据是用什么存储的。实际上,Animation的动画函数getTransformation目的在于生成当前帧的一个Transformation,这个Transformation采用alpha以及Matrix存储了一帧动画的数据,Transformation包含两种模式:
alpha模式:用于支持透明度动画
matrix模式:用于支持缩放、平移以及旋转动画
同时,Transformation还提供了许多两个接口用于组合多个Transformation:
compose:前结合(alpha相乘、矩阵右乘、边界叠加)
postCompose:后结合(alpha相乘、矩阵左乘、边界叠加)
至此,Animation本身算介绍完整了,还差一个可用于从XML中构建动画以及插间器的AnimationUtils,这里就不做具体分析了,有兴趣的同学可以自行研究。但是,到现在为止,我们还没讲明白是:getTransformation这个函数究竟是在哪里调用的?计算得到的动画数据又是怎么被应用的?慌不要慌,待我娓娓道来,当这些问题揭秘之后,我们就知道为什么Animation这个包要放在android.view下面以及Animation完成之后为什么View本身的属性不会被改变,于是也就知道插间动画(Animation)跟属性动画(Animator)本质上的区别在哪了。
4. Animation的调用
要了解Animation的调用源头,要从Animation的基本使用View.startAnimation开始寻根溯源:
public void startAnimation(Animation animation) {
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
setAnimation(animation);
invalidateParentCaches();
invalidate(true);
通过invalidate(true)函数会触发View的重新绘制,由于View的绘制流程并不是本文的重点,因此这里仅说明从View.draw是怎么走到对Animation的处理函数的:
View.draw(Canvas)—& ViewGroup.dispatchDraw(Canvas)—& ViewGroup.drawChild(Canvas, View, long)—& View.draw(Canvas, ViewGroup, long)—& View.applyLegacyAnimation(ViewGroup, long, Animation, boolean)
而View.applyLegacyAnimation就是Animation大显神通的舞台,其核心代码主要分三个部分:
初始化Animation(仅初始化一次)
调用Animation.initialize(width, height, parentWidth, parentHeight),通过View及ParentView的Size来解析Animation中的相关数据;
调用Animation.initializeInvalidateRegion(left, top, right, bottom)来设定动画的初始区域,并在fillBefore为true时计算Animation动画进度为0.0f的数据
调用getTransformation根据当前绘制事件生成Animation中对应帧的动画数据
根据动画数据设定重绘制区域
若仅为Alpha动画,此时动画区域为View的当前区域,且不会产生变化
若包含非Alpha动画,此时动画区域需要调用Animation.getInvalidateRegion进行计算,该函数会根据上述生成动画数据Thransformation中的Matrix进行计算,并与之前的动画区域执行unio操作,从而获取动画的完整区域
调用ViewGroup.invalidate(int l, int t, int r, int b)设定绘制区域
当View.applyLegacyAnimation调用完成之后,View此次绘制的动画数据就构建完成,之后便回到View.draw(Canvas, ViewGroup, long)应用动画数据对视图进行绘制刷新,其核心代码如下:
if (transformToApply != null) {
if (concatMatrix) {
if (drawingWithRenderNode) {
// 应用动画数据
renderNode.setAnimationMatrix(transformToApply.getMatrix());
canvas.translate(-transX, -transY);
// 应用动画数据
canvas.concat(transformToApply.getMatrix());
canvas.translate(transX, transY);
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
float transformAlpha = transformToApply.getAlpha();
if (transformAlpha & 1) {
// 应用动画数据
alpha *= transformA
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
重点来了,大家看到Animation产生的动画数据实际并不是应用在View本身的,而是应用在RenderNode或者Canvas上的,这就是为什么Animation不会改变View的属性的根本所在。另一方面,我们知道Animation仅在View被绘制的时候才能发挥自己的价值,这也是为什么插间动画被放在Android.view包内,因为它跟View是真心相爱的。文章到这,其实差不多可以结束了,但是创作动画过程中总是会被用到的一个神器还没出现,这让我有些不舍,尽管有太多人讲解这一神器,但是我还是毅然决然地决定抄一遍书,一来表示我对这一神器的爱,另一方面也是希望让文章更完整。
5. 插间器(Interpolator)
如果没有插间器,Animation应该按照时间来线性计算每一个时间点的动画帧数据;当时当加入插件器之后,我们计算动画帧数据时就可以更加的富有创造力,我可以随心所欲地计算任一时间点的动画帧数据,可以新加速在减速,也可以先减速在加速,总之一句话,我的地盘我做主。按照剧情的发展,接下来我应该介绍常用插间器了,但是作为一个有态度的程序员,我是不会按常理出牌的,想要了解常用插间器的实现原理,建议阅读。
其实很早之前就看过Animation的源码,但是当时因为懒并没有写文章做笔记,这次因为项目需要优化动画,于是又重新撸了一遍,在此撰文为记,以备后用。当然,也希望这篇分享能给大家一些收获,非常感谢你的阅读,如果有浪费到你的时间,也就浪费了,权当看了一章凑字数的小说,233333~~~
卖身不卖艺(捂脸~逃)
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
被以下专题收入,发现更多相似内容:
玩转简书的第一步,从这个专题开始。
想上首页热门榜么?好内容想被更多人看到么?来投稿吧!如果被拒也不要灰心哦~入选文章会进一个队...
· 147705人关注
分享Android开发的知识,教程,解析,前沿信息,都可以,欢迎大家投稿~
内容可搞笑,可逗比,另外欢迎申请管理员
· 24944人关注
Android老鸟给新人的建议、资源。
更优质的原创内容,欢迎关注技术公众号,微信搜索:“Open软件开发小组”或者“open_dev”
· 14763人关注
卖身不卖艺(捂脸~逃)
选择支付方式:Android 属性动画详解,属性动画基本用法!
Android 属性动画详解,属性动画基本用法!
Hello,大家好,今天要给大家讲的是Android 属性动画详解!
在Tween动画的讨论中,我们提到在Android中动画可以分为三类:①帧动画②Tween(补间动画)③Property Animation(属性动画),在前面的文章中,分别对帧动画和Tween动画进行了非常详细的讨论,如果有兴趣可以去上面的链接去阅读。那么今天就来和大家一起讨论下Property Animation,相信通过本系列博客的讨论你将对Android中的动画有个非常详细的了解。
通过本篇博客你将学到以下内容:
①为什么要引入属性动画
②属性动画的基本用法
③属性动画的监听器
④组合动画的实现
⑤属性动画的XML实现
1、为什么要引入属性动画
首先来看为什么要引入属性动画,我相信很多人跟我一样,看到属性动画,在脑海里闪现的第一个问题就是为什么要引入属性动画?我们都知道Android中已经有帧动画和补间动画了,那么为什么还要引入属性动画呢?要想得到这个问题的正确答案,无疑要去谷歌的官网了,首先我们来看看官网(官网地址)对Property
Animation与补间动画的区别进行的介绍:
补间动画只提供了对View进行增加动画的能力,所以如果你想对除View之外的其它对象做动画,你必须实现你自己的代码来达到这样的效果。另外,补间动画只能对View的几个方面进行动画的添加,例如View的缩放和旋转,而不是View的背景颜色等等。
补间动画的另一个缺点是它只修改了视图绘制的地方,而不是实际View的本身。比如,你给Button定义了一个在屏幕上移动的动画,这个Button的绘制是正确的,但是这个Button实际可以点击的位置是没有改变的,所以你必须用你自己的逻辑来解决这个问题。
使用属性动画这些约束将完全被解除,并且你可以对任何对象(Views and non-Views)的任何属性添加动画,并且这个对象的本身实际也是改变的。从更高层次上来说,你可以选择你想要的属性,来给其添加动画,如颜色、位置或大小,并且你可以通过插&#20540;器或者多个动画的同步,来定义你所需要的动画。
然而补间动画需要较少的时间来设置,并且也需要更少的代码。如果补间动画完成了你所需要做的一切或者现有的代码就是按照你想要的方式工作的,那么你没有必要使用属性动画。针对不同的情况有时候也许需要这两种动画进行工作才是有意义的。
以上三段就是官网给出的属性动画与补间动画的区别,可能看着比较费劲,其实引入属性动画主要有三点原因:
①因为补间动画只能对View进行操作,而不能对一个对象的属性,如颜色等进行操作,而属性动画可以,并且属性动画的操作范围不仅仅是View,它可以是任何对象。
②补间动画只能对View的几个方面做动画,也就是说补间动画不仅把范围缩小到View,而且并不是能对View的各个方面做动画,而只能是alpha(渐变)、scale(缩放)、translate(位移)、rotate(旋转)这四种动画。
③补间动画只是改变了View绘制的地方,而并没有真正改变View本身。什么意思?假如手机屏幕上有一个View,我们让他做补间动画向右移动20px,我们会看到这个View向右移动了20px,而此时你会发现这个View是不能响应你的点击事件的,只有你点击原来的位置才能触发这个View的点击事件。因为这个View实际还在原来的位置,只不过补间动画将这个View绘制的地方向右移动了20px,而这个View真正的属性并没有改变。
通过上面的介绍,相信大家已经明白了属性动画产生的原因,了解了它产生的背景之后,接下来的一步就是要学习它的用法了。
2、属性动画的介绍
属性动画常用的有两个类分别是ValueAnimator和ObjectAnimator,它的继承关系图如下:
从继承关系图中我们可以清晰的看到ValueAnimator和AnimatorSet是继承与抽象类Animator的,而ObjectAnimator和TimeAnimator是继承自ValueAnimator的。这个继承关系图,大家要好好识记一下,后面会用到,知道这些关系后,我们的讨论的方向就更加明确了,总共就这么几个类,逐一来看看呗。
3、ValueAnimator的基本使用
在学习ValueAnimator的基本使用之前,先来看下它的一些常用的方法
在上面的方法中,可能大家比较陌生的就是of开头的那几个,其中ofFloat,ofInt它们接收的参数类型都是可变参,所以我们可以传入任何数量的&#20540;;传进去的&#20540;列表,就表示动画时的变化范围;比如ofInt(3,9,6)就表示从数&#20540;3变化到数&#20540;9再变化到数&#20540;6。而ofObject接收两个参数一个TypeEvaluator类型的,另一个是Object类型的可变参数,关于TypeEvaluator,后续的文章会有详细的讨论。然后就是ofPropertyValuesHolder多属性动画同时工作管理类,有时候我们需要对一个对象的多个属性做动画,此时就会用到它。setFrameDelay设置多长时间刷新一帧,默认是10ms。但最终依赖系统的当前状态,一般我们不用管。
说了这么多废话,到底怎么用,其实ValueAnimator的使用非常简单,首先来看个最基础的用法,假如我们想创建一个&#20540;从0到1的动画,动画的时长为200毫秒,代码应该这样写:
执行上面的代码就执行了一个&#20540;从0到1平滑过渡的动画,从上面的代码中可以看出它并没有与任何的控件的任何属性有关系,从它的名字也能看出来它是对&#20540;做平滑过渡的,我们怎么知道呢?很简单只需要对它做监听就可以了,我们只需要添加如下代码:
在上述代码中我们给valueAnimator添加了一个addUpdateListener监听,在监听的回调中,回调给我们的是当前状态的ValueAnimator 的实例,得到这个实例后通过调用getAnimatedValue就可以拿到当前的&#20540;,然后将其打印,这里有一点需要提醒大家注意的是拿到的这个&#20540;的类型是与of..后的&#20540;得类型相对应的,ofFloat拿到的就是float类型,ofInt拿到的就是int类型。
运行上述代码打印结果如下:
从打印结果中可以看到valueAnimator的&#20540;在200毫秒内从0逐渐变化到了1,这些中间的过程谷歌已经帮我们实现好了。
在上面我们提到ofFloat(float… values)接收的参数类型是可变参,也就是说这里传递的参数的个数是没有限制,我们也可以传递多个参数,比如
上述代码就表示在200毫秒内,valueAnimator的&#20540;从0变化到3,然后再变化到1。ofInt的使用与ofFloat类&#20284;,只不过传的&#20540;的类型不同。
到这里可能有的同学会问,说了半天没有看到ValueAnimator做一个动画啊,那接下来就让一个View做位移动画,代码如下:
在上述代码中通过对valueAnimator添加监听,拿到当前帧的&#20540;后,不断的设置ImageView的TranslatonX(该View在X轴的偏移量)&#20540;,从而让其移动。它的运行效果如下:
可以看到我们通过使用ValueAnimator实现了在3秒内在X轴方向上移动100px的效果。这个动画的操作是在ValueAnimator的监听中实现的。
小总结: ValueAnimator是计算动画过程中变化的&#20540;,包含动画的开始&#20540;,结束&#20540;,持续时间等属性。但是这些&#20540;与我们的控件是无关的,要想把计算出来的&#20540;应用到对象上,必须为ValueAnimator注册一个监听器,该监听器负责更新对象的属性&#20540;。在实现这个监听器的时候,可以通过getAnimatedValue()的方法来获取当前动画的&#20540;,拿到这个&#20540;后,我们就可以为所欲为了。
4、ObjectAnimator的基本用法
相比于ValueAnimator,在开发中可能ObjectAnimator要比ValueAnimator用的多,因为ObjectAnimator可以直接操作对象的属性,而不用像ValueAnimator那么麻烦。
假如让一个ImageView做旋转的动画,代码可以这样写:
从上述代码我们可以看到ObjectAnimator与ValueAnimator的用法有点相&#20284;,又有不同,在上述代码中objectAnimator调用了ofFloat()方法来去创建一个ObjectAnimator的实例,与ValueAnimator不同的是,这里的ofFloat()方法当中接收的参数有点变化了。这里第一个参数要求传入一个object对象,即进行动画的对象,在上面我们传了一个ImageView。第二个参数是属性的名字,因为做旋转动画所以这里传的属性的名字为“rotation”。后面就是可变参数了,这里我们传的是0,360,表示让ImageView旋转360度,然后设置时长,调用start方法。美女效果如下,啊,不是,是运行效果如下:
可以看到美女还是不错的,啊。。不是,是运行效果还是不错的。
假如想看到透明度渐变的效果呢,代码可以这么写:
运行效果如下:
在上面的代码中我们设置里的“alpha”属性,让其在3秒内完成透明度从0到1的变化。
到这里从总体上看,属性动画的用法还是比较简单的,肯定有的童鞋会有疑问,ofFloat中的第二个参数都是能传哪些&#20540;呢?上面的代码中传了个“alpha”和”rotation”,但是究竟它能传哪些&#20540;呢?这一点从其名字中可以看出“属性”动画,无疑它是操作对象的属性的,所以它可以接收任意&#20540;,但是这里有一个前提,那就是这个属性必须要有get和set方法,什么意思呢?属性动画针对我们传入的属性&#20540;,比方说“alpha”,它会去寻找这个属性名所对应的get和set方法,内部会通过java反射机制来调用set函数修改对象属性&#20540;。由此我们可以推断出ImageView中肯定会有对alpha属性的get和set操作,通过寻找你会发现这两个方法在ImageView的父类View中,通过寻找在View中确实找到了这两个方法如下:
这也进一步验证我们的说法,到这里我们也知道,所有继承自View的控件都可以进行alpha变换,因为View中就有getAlpha和setAlpha方法。也许到这有的童鞋还会心有余悸心想上述说的我理解了,但是假如说我想对View的属性进行变换,不可能每次都要去View的源码里去看看它有没有get和set方法吧,这里呢,对经常用到的属性做一个小的总结:
①translationX和translationY:表示在X轴或者Y轴方向上的位移
② scaleX和scaleY:表示在X轴或者Y轴方向上的缩放
③rotation、rotationX和rotationY:这三个属性控制View对象围绕支点进行2D和3D旋转。
④ pivotX和pivotY:这两个属性控制着View对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认情况下,该支点的位置就是View对象的中心点。
⑤x和y:这是两个简单实用的属性,它描述了View对象在它的容器中的最终位置,它是最初的左上角坐标和translationX和translationY&#20540;的累计和。
⑥ alpha:它表示View对象的alpha透明度。默认&#20540;是1(不透明),0代表完全透明(不可见)。
当然我们可以操作的属性远远不止这些,任何属性只要有get和set方法,我们都可以操作。
ObjectAnimator是属性动画框架中最重要的实行类,创建一个ObjectAnimator只需通过他的静态工厂类直接返回一个ObjectAnimator对象。传的参数包括一个对象和对象的属性名字,但这个属性必须有get和set函数,还包括属性的初始&#20540;,最终&#20540;,还可以调用setInterpolator设置曲线函数。
5、Animator监听
对于Animator的监听在上面的代码中也略有体现,我们通过调用addUpdateListener这个方法给ValueAnimator添加了一个监听,其实从ValueAnimator的源码中可以看出它总共是有两个监听器的,监听器相关源码:
这里有一点大家需要明白,大家可以回到开始我们给出的继承关系图,从继承关系图中我们可以看出
AnimatorSet和ValueAnimator是继承自Animator的,而ObjectAnimator是继承自ValueAnimator的。所以对于Animator类中的监听,AnimatorSet、ValueAnimator、ObjectAnimator都可以用,而ValueAnimator类中的监听,AnimatorSet中是没有的,而ObjectAnimator是继承自ValueAnimator的,所以ValueAnimator和ObjectAnimator都是可以调用的。理论说完,就上实例我们可以这样为属性动画添加AnimatorListener
可以看到AnimatorListener提供了对动画开始、动画重复、动画结束、取消动画做了监听。但是有时候我们并不需要这么多啊,比如我们只想监听动画的开始,假如用这种方法需要把这四个方法都重写才行,代码太冗余了,谷歌的攻城狮也是想到了这一点,给我们提供了一个适配器AnimatorListenerAdapter,有这个类我们就可以选择性的,根据需要添加监听了,比如我们只需要添加动画开始时的监听,我们可以这么做:
有木有比上面简化了很多,可以看出谷歌对属性动画的优化还是下了很多功夫的。
6、组合动画的实现
上面我们都是对一个对象进行单一的动画,但是一个很酷的动画往往需要多个动画协同完成,谷歌也是给我提供了多种实现方式,一起来看看吧。
要想完成多个动画协同工作需要借助AnimatorSet这个类,这个类主要提供了三个播放方法,play(),playSequentially(),playTogether()。其中playSequentially()表示多个动画按顺序执,它主要有两种形式的参数playSequentially(Animator… items)和playSequentially(List &Animator&
animator);一个是可变参数,另一个是动画集合。
playTogether()表示几个动画同时执行,它接收的参数类型与playSequentially()一致。最后就是play方法了,play方法接收一个Animator动画实例,play(Animator anim),调用它之后会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法
after(Animator anim) & 将现有动画插入到传入的动画之后执行
after(long delay) & 将现有动画延迟指定毫秒后执行
before(Animator anim) & 将现有动画插入到传入的动画之前执行
with(Animator anim) & 将现有动画和传入的动画同时执行
好了,理论完了之后就要联系实际了,那接下来我们来做一个这样的组合效果:让一张图片旋转出厂的同时伴随着渐变和缩放,代码可以这样写:
运行效果如下
可以看出它是渐变、旋转、缩放、三种动画的组合,效果还算不错。
接着我们来看下play的用法,与上述动画类&#20284;,我们来实现这样一个动画,让一张图片缩放旋转出厂,出厂之后让它消失,可以用play实现,代码如下:
运行效果如下:
这样我们就用play实现了一个比较不错的组合动画了。
7、xml文件实现
前面我们在学Tween动画的时候,我们是分两篇介绍的,一篇是xml文件配置的实现,一篇是代码的实现,上述我们都是用代码实现的属性动画,那么怎么配置xml文件实现的?它的实现也很简单,首先需要做的就是在res下建立一个animator文件夹,然后创建一个xml文件,/res/animator/roation.xml。
在xml文件中总共有可以用三个标签,与代码实现是对应着的
&animator& &对应代码中的ValueAnimator
&objectAnimator& &对应代码中的ObjectAnimator
&set& &对应代码中的AnimatorSet
那么它们都可以设置哪些属性&#20540;呢?
animator中的属性如下:
android:duration:表示动画播放的时长
android:valueFrom:动画属性开始的&#20540;;取&#20540;范围为float,int和color,如果未指定,动画开始属性通过属性的get方法获得。颜色用6位16进制数表示(例如:#333333)
android:valueTo:动画结束&#20540;;取&#20540;范围同valueFrom
android:startOffset:取&#20540;范围为int,动画的start方法被调用后,延迟多少毫秒执行。
android:repeatCount:动画重复的次数,可以设置为-1或者正整数,-1表示无限循环,假如我们设置成1,&font color=”#FF000000&表示重复执行一次,所以它总共会执行2次。
android:repeatMode:动画重复模式,取&#20540;为repeat和reverse;repeat表示正序重播,reverse表示倒序重播,这与前面讲的Tween动画是类&#20284;的。
android:valueType:表示参数&#20540;类型,取&#20540;为intType和floatType;与android:valueFrom、android:valueTo相对应。如果这里的取&#20540;为intType,那么android:valueFrom、android:valueTo的&#20540;也就要对应的是int类型的数&#20540;。float也是一样。如果如果android:valueFrom、android:valueTo的&#20540;设置为color类型的&#20540;,则不需要设置这个参数;
android:interpolator:设置加速器;
objectAnimator标签中的属性如下:
可以看到与animator中的属性是差不多的,这里多了一个
android:propertyName=”string”表示要做动画的属性名字。其它的属性与animator中的一致,就不再浪费口舌和篇幅了。
set标签中的属性如下:
set标签只有一个属性如下:
android:ordering=[“together” | “sequentially”],其中together表示set标签下的动画同时执行,而sequentially表示set标签下的动画逐个执行。
理论终于说完了,掌握了理论之后,就可以来看妹子了。
最后我们以一个用xml实现的组合动画结束本篇的内容,我们实现的效果是这样的,先让这个妹子进入到屏幕的正中央,然后让她旋转360度,再然后让她离开屏幕,离开屏幕的同时伴随着透明度的变化。先看效果:
效果还算比较炫酷吧, 这也算是一个稍微复杂一点的动画了,与之对应的xml配置内容如下:
怎样将其xml文件加载到程序中呢?代码也很简单,只需要这样写:
可以看到,直接调用AnimatorInflater的loadAnimator将xml文件加载进来,并给其设置目标对象,最后调用start方法启动,就完成了。xml文件的配置大家可以根据运行效果自己分析分析。
我的热门文章
即使是一小步也想与你分享

我要回帖

更多关于 循环流化床锅炉动画 的文章

 

随机推荐