找回密码
 会员注册
查看: 19|回复: 0

探究Android属性动画执行过程

[复制链接]

2万

主题

0

回帖

6万

积分

超级版主

积分
64021
发表于 2024-10-12 00:38:29 | 显示全部楼层 |阅读模式
1.引言 属性动画作为Android动画功能的一个重要组成部分,可以实现很多有趣的动画效果,理解属性动画的执行过程有助于我们更好地使用属性动画去实现需求。本文将从源码的角度去探索属性动画的实现过程,加深大家对其的认知和理解。2.属性动画相关的类 2.1 ValueAnimator这个类是实现属性动画的一个重要的类,通过ValueAnimator.ofFloat()、ValueAnimator.ofInt()、ValueAnimator.ofObject()、ValueAnimator.ofArgb()、ValueAnimator.ofPropertyValuesHolder()等方法可以获得ValueAnimator的对象,然后可以通过对这个对象的操作去实现动画。使用ValueAnimator实现属性动画,需要实现ValueAnimator.AnimatorUpdateListener()接口,并在onAnimationUpdate()方法内为要添加动画的对象设置属性值。2.2 ObjectAnimatorObjectAnimator是ValueAnimator的子类,可以操作目标对象的动画属性,这个类的构造函数支持采用参数的形式传入要使用动画的目标对象和属性名。3.属性动画的实现过程 ObjectAnimatorobjectAnimator=ObjectAnimator.ofFloat(iv,"alpha",1.0f,0f);objectAnimator.setDuration(3000);objectAnimator.start();这是一段简单的代码,它使用属性动画实现了一张图片的透明度渐变的效果,我们从这一段代码入手,去分析属性动画的实现过程。3.1 创建属性动画/*** target:添加动画效果的目标对象* propertyName:动画效果的属性名* values:动画将会在这个时间之间执行的数值集合*/publicstaticObjectAnimatorofFloat(Objecttarget,StringpropertyName,float...values){ObjectAnimatoranim=newObjectAnimator(target,propertyName);anim.setFloatValues(values);returnanim;}这个方法返回了一个属性动画对象,第一个参数是产生动画效果的目标对象,第二个参数是属性名,目标对象的属性名应该有与之对应的set()方法,例如我们传入属性名"alpha",那么这个目标对象也应该有setAlpha()方法。参数values传一个值的时候,这个值是动画的结束值,传两个数值的时候,第一个值是开始值,第二个值是结束值,多于两个值的时候,第一个值是开始值,最后一个值是结束值。privateObjectAnimator(Objecttarget,StringpropertyName){setTarget(target);setPropertyName(propertyName);}这个是属性动画的构造函数,里面执行了两个方法setTarget(target)和setPropertyName(propertyName)。@OverridepublicvoidsetTarget(@NullableObjecttarget){finalObjectoldTarget=getTarget();if(oldTarget!=target){if(isStarted()){cancel();}mTarget=target==nullnull:newWeakReference(target);//Newtargetshouldcausere-initializationpriortostartingmInitialized=false;}}publicvoidsetPropertyName(@NonNullStringpropertyName){//mValuescouldbenullifthisisbeingconstructedpiecemeal.Justrecordthe//propertyNametobeusedlaterwhensetValues()iscalledifso.if(mValues!=null){PropertyValuesHoldervaluesHolder=mValues[0];StringoldName=valuesHolder.getPropertyName();valuesHolder.setPropertyName(propertyName);mValuesMap.remove(oldName);mValuesMap.put(propertyName,valuesHolder);}mPropertyName=propertyName;//Newproperty/values/targetshouldcausere-initializationpriortostartingmInitialized=false;}mValues是一个PropertyValuesHolder数组,PropertyValuesHolder持有动画的属性名和属性值信息,mValuesMap是一个hashmap数组,用来管理PropertyValuesHolder对象,在调用getAnimatedValue(String)方法的时候,这个map通过属性名去查找动画执行的数值。当mValues不为空的时候,将属性名信息放入mValuesMap。//ObjectAnimator@OverridepublicvoidsetFloatValues(float...values){if(mValues==null||mValues.length==0){//Novaluesyet-thisanimatorisbeingconstructedpiecemeal.Initthevalueswith//whateverthecurrentpropertyNameisif(mProperty!=null){setValues(PropertyValuesHolder.ofFloat(mProperty,values));}else{setValues(PropertyValuesHolder.ofFloat(mPropertyName,values));}}else{super.setFloatValues(values);}}mValues为null或者数组元素个数为0的时候,调用其父类ValueAnimator的setValues()方法,在setValues()内执行了初始化mValues和mValuesMap的操作,并将PropertyValuesHolder放入mValuesMap。当mValues不为null且元素个数不为0的时候,调用其父类ValueAnimator的setFloatValues()方法,在setFloatValues()方法内满足条件又会调用到PropertyValuesHolder的setFloatValues()方法。//PropertyValuesHolderpublicvoidsetFloatValues(float...values){mValueType=float.class;mKeyframes=KeyframeSet.ofFloat(values);}这里的mValueType指的是提供的值的类型,mKeyframes是定义这个动画的关键帧集合。//KeyframeSetpublicstaticKeyframeSetofFloat(float...values){booleanbadValue=false;intnumKeyframes=values.length;FloatKeyframekeyframes[]=newFloatKeyframe[Math.max(numKeyframes,2)];if(numKeyframes==1){keyframes[0]=(FloatKeyframe)Keyframe.ofFloat(0f);keyframes[1]=(FloatKeyframe)Keyframe.ofFloat(1f,values[0]);if(Float.isNaN(values[0])){badValue=true;}}else{keyframes[0]=(FloatKeyframe)Keyframe.ofFloat(0f,values[0]);for(inti=1;i=0||mReversing){//Ifthere'snostartdelay,inittheanimationandnotifystartlistenersrightaway//tobeconsistentwiththepreviousbehavior.Otherwise,postponethisuntilthefirst//frameafterthestartdelay.startAnimation();if(mSeekFraction==-1){//Noseek,startatplaytime0.Notethatthereasonwearenotusingfraction0//isbecauseforanimationswith0duration,wewanttobeconsistentwithpre-N//behavior:skiptothefinalvalueimmediately.setCurrentPlayTime(0);}else{setCurrentFraction(mSeekFraction);}}}在这个方法内进行了一些赋值操作,addAnimationCallback(0)和startAnimation()是比较重要的操作。//ValueAnimatorprivatevoidaddAnimationCallback(longdelay){if(!mSelfPulse){return;}getAnimationHandler().addAnimationFrameCallback(this,delay);}这个方法内执行了AnimationHandler的addAnimationFrameCallback()方法注册回调,我们继续看看addAnimationFrameCallback()方法。//AnimationHandlerpublicvoidaddAnimationFrameCallback(finalAnimationFrameCallbackcallback,longdelay){if(mAnimationCallbacks.size()==0){getProvider().postFrameCallback(mFrameCallback);}if(!mAnimationCallbacks.contains(callback)){mAnimationCallbacks.add(callback);}if(delay>0){mDelayedCallbackStartTime.put(callback,(SystemClock.uptimeMillis()+delay));}}这个方法添加了一个AnimationFrameCallback回调,AnimationFrameCallback是AnimationHandler的一个内部接口,其中有两个重要的方法doAnimationFrame()和commitAnimationFrame()。//AnimationHandlerinterfaceAnimationFrameCallback{booleandoAnimationFrame(longframeTime);voidcommitAnimationFrame(longframeTime);}AnimationFrameCallback是可以收到动画执行时间和帧提交时间通知的回调,内有两个方法,doAnimationFrame()和commitAnimationFrame()。//AnimationHandlerprivateclassMyFrameCallbackProviderimplementsAnimationFrameCallbackProvider{finalChoreographermChoreographer=Choreographer.getInstance();@OverridepublicvoidpostFrameCallback(Choreographer.FrameCallbackcallback){mChoreographer.postFrameCallback(callback);}@OverridepublicvoidpostCommitCallback(Runnablerunnable){mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT,runnable,null);}@OverridepubliclonggetFrameTime(){returnmChoreographer.getFrameTime();}@OverridepubliclonggetFrameDelay(){returnChoreographer.getFrameDelay();}@OverridepublicvoidsetFrameDelay(longdelay){Choreographer.setFrameDelay(delay);}}前面的getProvider()方法获得了MyFrameCallbackProvider的一个实例,MyFrameCallbackProvider是AnimationHandler的一个内部类,实现了AnimationFrameCallbackProvider接口,使用Choreographer作为计时脉冲的提供者,去发送帧回调。Choreographer从显示器子系统获得时间脉冲,postFrameCallback()方法发送帧回调。//AnimationHandlerpublicinterfaceAnimationFrameCallbackProvider{voidpostFrameCallback(Choreographer.FrameCallbackcallback);voidpostCommitCallback(Runnablerunnable);longgetFrameTime();longgetFrameDelay();voidsetFrameDelay(longdelay);}//AnimationHandlerprivatefinalChoreographer.FrameCallbackmFrameCallback=newChoreographer.FrameCallback(){@OverridepublicvoiddoFrame(longframeTimeNanos){doAnimationFrame(getProvider().getFrameTime());if(mAnimationCallbacks.size()>0){getProvider().postFrameCallback(this);}}};在这个回调内执行了doAnimationFrame()方法,如果mAnimationCallbacks的个数大于0,AnimationFrameCallbackProvider就继续发送帧回调,继续重复执行doAnimationFrame()。//AnimationHandlerprivatevoiddoAnimationFrame(longframeTime){longcurrentTime=SystemClock.uptimeMillis();finalintsize=mAnimationCallbacks.size();for(inti=0;i=0){mOverallFraction=mSeekFraction;}else{mOverallFraction=0f;}if(mListeners!=null){notifyStartListeners();}}startAnimation()方法内调用了initAnimation()初始化动画。//ValueAnimatorpublicfinalbooleandoAnimationFrame(longframeTime){//省略部分代码...finallongcurrentTime=Math.max(frameTime,mStartTime);booleanfinished=animateBasedOnTime(currentTime);if(finished){endAnimation();}returnfinished;}这个方法在执行动画的过程中会被多次调用,其中重要的操作是animateBasedOnTime(currentTime)。//ValueAnimatorbooleananimateBasedOnTime(longcurrentTime){booleandone=false;if(mRunning){finallongscaledDuration=getScaledDuration();finalfloatfraction=scaledDuration>0(float)(currentTime-mStartTime)/scaledDuration:1f;finalfloatlastFraction=mOverallFraction;finalbooleannewIteration=(int)fraction>(int)lastFraction;finalbooleanlastIterationFinished=(fraction>=mRepeatCount+1)&(mRepeatCount!=INFINITE);if(scaledDuration==0){//0durationanimator,ignoretherepeatcountandskiptotheenddone=true;}elseif(newIteration&!lastIterationFinished){//Timetorepeatif(mListeners!=null){intnumListeners=mListeners.size();for(inti=0;i
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-26 01:04 , Processed in 0.476238 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表