属性动画的工作原理
来源:互联网 发布:网络交友网站 编辑:程序博客网 时间:2024/05/01 12:40
属性动画的工作原理
以ObjectAnimator.ofInt(button,”width”,500).setDuration(5000).start()为例。
ObjectAnimator#start()
@Overridepublic void start() { // See if any of the current active/pending animators need to be canceled AnimationHandler handler = sAnimationHandler.get(); if (handler != null) { //当前动画是否和目标动画相似,如果相似,就把相似的动画取消 int numAnims = handler.mAnimations.size(); for (int i = numAnims - 1; i >= 0; i--) { if (handler.mAnimations.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } //等待的动画是否和目标动画相似,如果相似,就把相似的动画取消 numAnims = handler.mPendingAnimations.size(); for (int i = numAnims - 1; i >= 0; i--) { if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } //延迟的动画是否和目标动画相似,如果相似,就把相似的动画取消 numAnims = handler.mDelayedAnims.size(); for (int i = numAnims - 1; i >= 0; i--) { if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } } //Log if (DBG) { Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration()); for (int i = 0; i < mValues.length; ++i) { PropertyValuesHolder pvh = mValues[i]; Log.d(LOG_TAG, " Values[" + i + "]: " + pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " + pvh.mKeyframes.getValue(1)); } } //调用父类的start super.start();}
ObjectAnimator继承了ValueAnimator,我们再来看super.start();
ValueAnimator#start()
private void start(boolean playBackwards) { if (Looper.myLooper() == null) { //表明需要运行在有Looper的线程中 throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } mPlayingBackwards = playBackwards; mCurrentIteration = 0; mPlayingState = STOPPED; mStarted = true; mStartedDelay = false; mPaused = false; updateScaledDuration(); // in case the scale factor has changed since creation time AnimationHandler animationHandler = getOrCreateAnimationHandler(); animationHandler.mPendingAnimations.add(this); if (mStartDelay == 0) { // This sets the initial value of the animation, prior to actually starting it running setCurrentPlayTime(0); mPlayingState = STOPPED; mRunning = true; notifyStartListeners(); } //animationHandler是一个Runnable,调用runnable.start(); //经过一系列的方法 // -> 调用DisplayEventReceiver#nativeScheduleVsync() [native方法] // -> 调用ValueAnimator.doAnimationFrame(); animationHandler.start();}
最终将调用ValueAnimator.doAnimationFrame();
final boolean doAnimationFrame(long frameTime) { if (mPlayingState == STOPPED) { mPlayingState = RUNNING; if (mSeekTime < 0) { mStartTime = frameTime; } else { mStartTime = frameTime - mSeekTime; // Now that we're playing, reset the seek time mSeekTime = -1; } } if (mPaused) { if (mPauseTime < 0) { mPauseTime = frameTime; } return false; } else if (mResumed) { mResumed = false; if (mPauseTime > 0) { // Offset by the duration that the animation was paused mStartTime += (frameTime - mPauseTime); } } // The frame time might be before the start time during the first frame of // an animation. The "current time" must always be on or after the start // time to avoid animating frames at negative time intervals. In practice, this // is very rare and only happens when seeking backwards. final long currentTime = Math.max(frameTime, mStartTime); //[重点方法] -> 在此方法中调用animateValue() return animationFrame(currentTime);}
在animationFrame中将调用animateValue()
void animateValue(float fraction) { fraction = mInterpolator.getInterpolation(fraction); mCurrentFraction = fraction; int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { //计算每帧动画所对应的属性值 mValues[i].calculateValue(fraction); } if (mUpdateListeners != null) { int numListeners = mUpdateListeners.size(); for (int i = 0; i < numListeners; ++i) { mUpdateListeners.get(i).onAnimationUpdate(this); } }}
mValues是一个PropertyValuesHolder数组,我们再来看PropertyValuesHolder
在初始化的时候,如果属性的初始值没有提供,则get方法将会被调用 [通过反射进行调用]
//初始化属性 private void setupValue(Object target, Keyframe kf) { if (mProperty != null) { Object value = convertBack(mProperty.get(target)); kf.setValue(value); } try { if (mGetter == null) { Class targetClass = target.getClass(); setupGetter(targetClass); if (mGetter == null) { // Already logged the error - just return to avoid NPE return; } } //通过反射进行调用 Object value = convertBack(mGetter.invoke(target)); kf.setValue(value); } catch (InvocationTargetException e) { Log.e("PropertyValuesHolder", e.toString()); } catch (IllegalAccessException e) { Log.e("PropertyValuesHolder", e.toString()); }}
当下一帧到来的时候,PropertyValuesHolder#setAnimatedValue方法会将新的属性设置给对象,调用其set方法。 [通过反射进行调用]
void setAnimatedValue(Object target) { if (mProperty != null) { mProperty.set(target, getAnimatedValue()); } if (mSetter != null) { try { mTmpValueArray[0] = getAnimatedValue(); //通过反射调用 mSetter.invoke(target, mTmpValueArray); } catch (InvocationTargetException e) { Log.e("PropertyValuesHolder", e.toString()); } catch (IllegalAccessException e) { Log.e("PropertyValuesHolder", e.toString()); } }}
重点
- 属性动画最终会调用ValueAnimator来进行处理
- 动画的本质是通过改变坐标和属性,快速刷新来形成动画的错觉
- 属性值的设置通过反射来进行
0 0
- 属性动画的工作原理
- 动画(四)属性动画的工作原理
- 动画(六)属性动画的工作原理
- Android属性动画工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- 动画四 Android源码分析—属性动画的工作原理
- [看书日记20151231]属性动画工作原理, 性能优化 & 对12月的感想
- 属性动画是如何工作的?
- 计算机CPU的工作原理动画
- Android 属性动画的原理分析
- android:layout_weight属性的工作原理
- Linux用户(User)和用户组(Group)管理命令
- ASM java字节码框架
- 深入浅出单实例SINGLETON设计模式
- c 标签简单用法
- VR系列——Oculus Publishing文档:一、App发布简介
- 属性动画的工作原理
- FastDFS NGINX集成与图片防盗
- HM-16.0的解码过程:将编码后的*.bin文件解码
- jquery 的attr
- javascript获取<a>*****</a>中的值,与a的超链接
- 软件测试笔记1-概念
- 拆分窗口后,视图如何与文档进行关联
- Redis-数据结构-2-链表
- "一个实体对象不能由多个IEntityChangeTracker实例引用" 推荐解决方案