属性动画源码分析
来源:互联网 发布:最新网络电视机 编辑:程序博客网 时间:2024/05/21 09:11
详见我的简书 http://www.jianshu.com/p/29fbf22bbbe3
分析版本api 24
首先我们要找一个入口,就从ObjectAnimator.ofInt(this, "width", 0, -20).start()开始吧,其他动画都是类似的。
先看参数构造
ObjectAnimator.ofInt(this, "currentProgress", 0, progress)
/ * * @param target The object whose property is to be animated. * @param property The property being animated. * @param values A set of values that the animation will animate between over time. * @return An ObjectAnimator object that is set up to animate between the given values. */ public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) { ObjectAnimator anim = new ObjectAnimator(target, property); anim.setIntValues(values); return anim; }
通过target, property构造了ObjectAnimator,并设置了可变int数组values ,继续看 anim.setIntValues(values);
@Override public void setIntValues(int... values) { if (mValues == null || mValues.length == 0) { // No values yet - this animator is being constructed piecemeal. Init the values with // whatever the current propertyName is if (mProperty != null) { setValues(PropertyValuesHolder.ofInt(mProperty, values)); } else { setValues(PropertyValuesHolder.ofInt(mPropertyName, values)); } } else { super.setIntValues(values); } }
a 继续数组为空时,调用自身的setIntValues(values),
public void setValues(PropertyValuesHolder... values) { int numValues = values.length; mValues = values; mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues); for (int i = 0; i < numValues; ++i) { PropertyValuesHolder valuesHolder = values[i]; mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder); } // New property/values/target should cause re-initialization prior to starting mInitialized = false; }
b 否则父类的ValueAnimator的public void setValues(PropertyValuesHolder... values)
public void setIntValues(int... values) { if (values == null || values.length == 0) { return; } if (mValues == null || mValues.length == 0) { setValues(PropertyValuesHolder.ofInt("", values)); } else { PropertyValuesHolder valuesHolder = mValues[0]; valuesHolder.setIntValues(values); } // New property/values/target should cause re-initialization prior to starting mInitialized = false; }
还是走上一个函数a public void setValues(PropertyValuesHolder... values)最终将数据塞给mValuesMap保存下来
正式start()
public 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(); } } } } 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)); } } super.start(); }
这段代码逻辑还是比较清楚的,在比较与mAnimations,mPendingAnimations,mDelayedAnims数组中找出是否存在当前动画,先cancel掉。
接着log,然后 super.start();
@Override public void start() { start(false); }
private void start(boolean playBackwards) { if (Looper.myLooper() == null) { throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } mReversing = playBackwards; mPlayingBackwards = playBackwards; if (playBackwards && mSeekFraction != -1) { if (mSeekFraction == 0 && mCurrentIteration == 0) { // special case: reversing from seek-to-0 should act as if not seeked at all mSeekFraction = 0; } else if (mRepeatCount == INFINITE) { mSeekFraction = 1 - (mSeekFraction % 1); } else { mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction); } mCurrentIteration = (int) mSeekFraction; mSeekFraction = mSeekFraction % 1; } if (mCurrentIteration > 0 && mRepeatMode == REVERSE && (mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) { // if we were seeked to some other iteration in a reversing animator, // figure out the correct direction to start playing based on the iteration if (playBackwards) { mPlayingBackwards = (mCurrentIteration % 2) == 0; } else { mPlayingBackwards = (mCurrentIteration % 2) != 0; } } int prevPlayingState = mPlayingState; 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 if (prevPlayingState != SEEKED) { setCurrentPlayTime(0); } mPlayingState = STOPPED; mRunning = true; notifyStartListeners(); } animationHandler.start(); }
AnimationHandler是一个单例对象,getOrCreateAnimationHandler()获取。 不同属性动画版本,这里有所区别。 animationHandler.start();
public void start() { scheduleAnimation(); }
继续看
private void scheduleAnimation() { if (!mAnimationScheduled) { mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null); mAnimationScheduled = true; } }
原来是在这里mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null); Choreographer机制,用于同Vsync机制配合,实现统一调度界面绘图
最终调用
private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { if (DEBUG_FRAMES) { Log.d(TAG, "PostCallback: type=" + callbackType + ", action=" + action + ", token=" + token + ", delayMillis=" + delayMillis); } synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); if (dueTime <= now) { scheduleFrameLocked(now); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); msg.arg1 = callbackType; msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, dueTime); } } }
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);根据类型加入不同的队列
Android系统Choreographer机制实现过程
当VSYNC信号到达时,Choreographer doFrame()函数被调用。代码较多,中间省略掉
void doFrame(long frameTimeNanos, int frame) { ... try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame"); mFrameInfo.markInputHandlingStart(); doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); mFrameInfo.markAnimationsStart(); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); mFrameInfo.markPerformTraversalsStart(); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } if (DEBUG_FRAMES) { final long endNanos = System.nanoTime(); Log.d(TAG, "Frame " + frame + ": Finished, took " + (endNanos - startNanos) * 0.000001f + " ms, latency " + (startNanos - frameTimeNanos) * 0.000001f + " ms."); } }
于是
void doCallbacks(int callbackType, long frameTimeNanos) { CallbackRecord callbacks; synchronized (mLock) { final long now = SystemClock.uptimeMillis(); //从指定类型的CallbackQueue队列中查找执行时间到的CallbackRecord callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now); if (callbacks == null) { return; } mCallbacksRunning = true; } try { //由于CallbackQueues是按时间先后顺序排序的,因此遍历执行所有时间到的CallbackRecord 。 这个record是CallbackQueues post的时候加入的。 for (CallbackRecord c = callbacks; c != null; c = c.next) { c.run(frameTimeNanos); } } finally { synchronized (mLock) { mCallbacksRunning = false; do { final CallbackRecord next = callbacks.next; recycleCallbackLocked(callbacks); callbacks = next; } while (callbacks != null); } } }
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);
}
这句即调用mAnimate的run方法
final Runnable mAnimate = new Runnable() { @Override public void run() { mAnimationScheduled = false; doAnimationFrame(mChoreographer.getFrameTime()); } };
继续走doAnimationFrame(mChoreographer.getFrameTime());
void doAnimationFrame(long frameTime) { mLastFrameTime = frameTime; // mPendingAnimations holds any animations that have requested to be started // We're going to clear mPendingAnimations, but starting animation may // cause more to be added to the pending list (for example, if one animation // starting triggers another starting). So we loop until mPendingAnimations // is empty. while (mPendingAnimations.size() > 0) { ArrayList<ValueAnimator> pendingCopy = (ArrayList<ValueAnimator>) mPendingAnimations.clone(); mPendingAnimations.clear(); int count = pendingCopy.size(); for (int i = 0; i < count; ++i) { ValueAnimator anim = pendingCopy.get(i); // If the animation has a startDelay, place it on the delayed list if (anim.mStartDelay == 0) { anim.startAnimation(this); } else { mDelayedAnims.add(anim); } } } // Next, process animations currently sitting on the delayed queue, adding // them to the active animations if they are ready int numDelayedAnims = mDelayedAnims.size(); for (int i = 0; i < numDelayedAnims; ++i) { ValueAnimator anim = mDelayedAnims.get(i); if (anim.delayedAnimationFrame(frameTime)) { mReadyAnims.add(anim); } } int numReadyAnims = mReadyAnims.size(); if (numReadyAnims > 0) { for (int i = 0; i < numReadyAnims; ++i) { ValueAnimator anim = mReadyAnims.get(i); anim.startAnimation(this); anim.mRunning = true; mDelayedAnims.remove(anim); } mReadyAnims.clear(); } // Now process all active animations. The return value from animationFrame() // tells the handler whether it should now be ended int numAnims = mAnimations.size(); for (int i = 0; i < numAnims; ++i) { mTmpAnimations.add(mAnimations.get(i)); } for (int i = 0; i < numAnims; ++i) { ValueAnimator anim = mTmpAnimations.get(i); if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) { mEndingAnims.add(anim); } } mTmpAnimations.clear(); if (mEndingAnims.size() > 0) { for (int i = 0; i < mEndingAnims.size(); ++i) { mEndingAnims.get(i).endAnimation(this); } mEndingAnims.clear(); } // Schedule final commit for the frame. mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null); // If there are still active or delayed animations, schedule a future call to // onAnimate to process the next frame of the animations. if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) { scheduleAnimation(); } }
这里有好几个重要的逻辑,其实处理的主要调度在这里发出:
1 首先遍历mPendingAnimations队列 :
a 要延迟启动,加入mDelayedAnims队列
b 如果不是延迟则调用startAnimation(AnimationHandler handler)方法
2 接着处理mDelayedAnims延迟队列,经过delayedAnimationFrame判断是否唤醒当前的动画
如果唤醒的话将ValueAnimator对象添加到mReadyAnims准备列表中;
3 接下来处理mReadyAnims列表,遍历该列表取出ValueAnimator对象并调用startAnimation(AnimationHandler handler)方法
4 下一步将处理mAnimations动画列表,通过遍历将当前要启动的动画倒装在临时列表mTmpAnimations,遍历临时列表调用anim.doAnimationFrame(frameTime)方法,通过该方法的返回值判断是否为动画的最后一帧,若是,则将ValueAnimator对象添加到mEndingAnims结束动画列表。遍历结束后清除临时列表mTmpAnimations;
5 最后遍历mEndingAnims结束列表调用endAnimation()方法,遍历结束后清除mEndingAnims列表。
6 mAnimations和mDelayedAnims两个列表,只要任何一个不为空还会调用scheduleAnimation方法,形成了循环。
参考 http://icedcap.github.io/2016/06/21/Android%E5%8A%A8%E7%94%BB%E5%AE%8C%E5%85%A8%E6%80%BB%E7%BB%93/
先看ValueAnimator的startAnimation(AnimationHandler handler),好吧,一系列初始化
这里先initAnimation之后就notifyStartListeners
先看listener
原来是这里回调listener开始动画。
接着继续看,动画执行:
final boolean doAnimationFrame(long frameTime) { if (mPlayingState == STOPPED) { mPlayingState = RUNNING; if (mSeekFraction < 0) { mStartTime = frameTime; } else { long seekTime = (long) (mDuration * mSeekFraction); mStartTime = frameTime - seekTime; mSeekFraction = -1; } mStartTimeCommitted = false; // allow start time to be compensated for jank } 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); mStartTimeCommitted = false; // allow start time to be compensated for jank } } // 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); return animationFrame(currentTime); }
boolean animationFrame(long currentTime) { boolean done = false; switch (mPlayingState) { case RUNNING: case SEEKED: float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f; if (mDuration == 0 && mRepeatCount != INFINITE) { // Skip to the end mCurrentIteration = mRepeatCount; if (!mReversing) { mPlayingBackwards = false; } } if (fraction >= 1f) { if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) { // Time to repeat if (mListeners != null) { int numListeners = mListeners.size(); for (int i = 0; i < numListeners; ++i) { mListeners.get(i).onAnimationRepeat(this); } } if (mRepeatMode == REVERSE) { mPlayingBackwards = !mPlayingBackwards; } mCurrentIteration += (int) fraction; fraction = fraction % 1f; mStartTime += mDuration; // Note: We do not need to update the value of mStartTimeCommitted here // since we just added a duration offset. } else { done = true; fraction = Math.min(fraction, 1.0f); } } if (mPlayingBackwards) { fraction = 1f - fraction; } animateValue(fraction); break; } return done; }
mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE 条件内回调了onAnimationRepeat(this)。 而改函数继续调用了 animateValue(fraction); fraction 动画进度。然后先调用子类ObjectAnimator的animateValue
注意到先调用了ValueAnimator super.animateValue(fraction);
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); } } }
calculateValue将根据Keyframes获取到的值塞给mAnimatedValue
void calculateValue(float fraction) { Object value = mKeyframes.getValue(fraction); mAnimatedValue = mConverter == null ? value : mConverter.convert(value); }
这里回调了动画的进度mUpdateListeners.get(i).onAnimationUpdate(this),很多时候可以根据这个回调来完成我们想要的一些效果,如贝塞尔曲线运动等。animateValue然后回到子类ObjectAnimator的void animateValue(float fraction) 方法
此时调用PropertyValuesHolder的 void setAnimatedValue(Object target)
可见是使用反射方式给对应属性值设置了value值。
里面的逻辑比较清晰,但是调用太多了,读起来还是比较费劲的。最后给出一下整个的时序:
Animator#start() --> ValueAnimator#start() --> ValueAnimator# start(false) --> AnimationHandler#start()--> scheduleAnimation() -->
--> Choreographer#postCallback(Choreographer.CALLBACK_ANIMATION ....(省略) -->
AnimationHandler # void doAnimationFrame(long frameTime)--> ValueAnimator # startAnimation(AnimationHandler) -->ValueAnimator # notifyStartListeners --> AnimatorListener#onAnimationStart() -->ValueAnimator # doAnimationFrame() -->
ValueAnimator # animationFrame(currentTime) --> ObjectAnimator#animateValue(fraction) --> ValueAnimator # animateValue(fraction) --> AnimatorListener#onAnimationUpdate(this) -->PropertyValuesHolder # setAnimatedValue(target)
- 属性动画源码分析
- 属性动画源码分析
- Android属性动画ValueAnimator源码简单分析
- Android属性动画ObjectAnimator源码简单分析
- Android属性动画AnimatorSet源码简单分析
- Android属性动画ObjectAnimator源码简单分析
- 属性动画(ObjectAnimator)源码分析
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- 属性动画(Property Animation)深入分析(原理、源码、实践)
- 安卓 属性动画 ValueAnimator ObjectAnimator 源码分析 关键处
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android属性动画,从源码的角度分析
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- 行级标签、块级标签和行内块级标签
- LeetCode
- ios上传appStore所需各种图片尺寸
- UVA
- 编写方法输出个人信息
- 属性动画源码分析
- 学前段半个月的一些心得(一)
- mybaties中foreach数据批量更新
- WUST OJ 1677:约会(思维)
- Opencv访问图像像素
- margin的塌陷现象跟合并现象
- 如何获取方法参数的泛型类型
- 1.HTML表单及列表
- 如何使用 PDF EXPERT 压缩 PDF 让PDF文档变?