ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# Property Animation ## 属性动画的优点 - 属性动画顾名思义就是改变了View的属性,而不仅仅是绘制的位置。 - 属性动画可以操作的属性相比于补间动画大大增加,除了常用的平移、旋转、缩放、透明度还有颜色等,基本上能通过View.setXX来设置的属性,属性动画都可以操作,这大大增加了我们在使用动画时的灵活性。 - 属性动画分为ObjectAnimator和ValueAnimator,其中ObjectAnimator是继承于ValueAnimator。 ## ValueAnimator > ValueAnimator并不会改变属性的大小,他只是在一段时间生成某些值。我们需要做的是监听这些值得改变从而该改变View的属性,进而产生动画效果。 下边的动画就是对mView进行平移: ``` ValueAnimator animator = ValueAnimator.ofFloat(0, 1000); anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mView.setTranslationX(animation.getAnimatedValue()); } }); animator.setDuration(1000).start() ``` ## ObjectAnimator > 在ValueAnimator的基础之上,对控件的某个属性执行一次动画。 相同的对mView进行平移的动画ObjectAnimator是这样实现的: ``` ObjectAnimator animator=ObjectAnimator.ofFloat (mView,"translationX",0,1000); animator.setDuration (1000); animator.start (); ``` ## PropertyAnimation流程图 ![](../images/PropertyAnimation流程图.png) ## 属性动画代码的执行过程 ### start #### ObjectAnimator.start ``` public final class ObjectAnimator extends ValueAnimator { /***部分代码省略***/ @Override 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(); } } } /***部分代码省略***/ } /***部分代码省略***/ //然后调用ValueAnimator.start()方法 super.start(); } } ``` #### ValueAnimator.start ``` public class ValueAnimator extends Animator { /***部分代码省略***/ protected static ThreadLocal<AnimationHandler> sAnimationHandler = new ThreadLocal<AnimationHandler>(); //保证每个线程有且只有一个AnimationHandler private static AnimationHandler getOrCreateAnimationHandler() { AnimationHandler handler = sAnimationHandler.get(); if (handler == null) { handler = new AnimationHandler(); sAnimationHandler.set(handler); } return handler; } @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"); } /***部分代码省略***/ //创建或者获取animationHandler实例 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(); } //回调监听器,通知动画开始 private void notifyStartListeners() { if (mListeners != null && !mStartListenersCalled) { ArrayList<AnimatorListener> tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone(); int numListeners = tmpListeners.size(); for (int i = 0; i < numListeners; ++i) { tmpListeners.get(i).onAnimationStart(this); } } mStartListenersCalled = true; } public void setCurrentPlayTime(long playTime) { float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration : 1; setCurrentFraction(fraction); } public void setCurrentFraction(float fraction) { //初始化动画 initAnimation(); if (fraction < 0) { fraction = 0; } /***部分代码省略***/ } } ``` #### AnimationHandler.start ``` public class ValueAnimator extends Animator { /***部分代码省略***/ protected static class AnimationHandler implements Runnable { /***部分代码省略***/ //开始动画 public void start() { scheduleAnimation(); } //发送VSYNC信号回调请求 private void scheduleAnimation() { if (!mAnimationScheduled) { mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null); mAnimationScheduled = true; } } // Called by the Choreographer. //Choreographer的VSYNC信号回调 @Override public void run() { mAnimationScheduled = false; doAnimationFrame(mChoreographer.getFrameTime()); } private void doAnimationFrame(long frameTime) { /***部分代码省略***/ // 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); //执行动画 //doAnimationFrame方法返回ture,则该动画添加在mEndingAnims队列中进行end操作 if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) { mEndingAnims.add(anim); } } /***部分代码省略***/ //循环执行,直到endAnimation将mAnimations置空 if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) { scheduleAnimation(); } } } } ``` ### init #### ObjectAnimator.initAnimation ``` public final class ObjectAnimator extends ValueAnimator { /***部分代码省略***/ @Override void initAnimation() { if (!mInitialized) { // mValueType may change due to setter/getter setup; do this before calling super.init(), // which uses mValueType to set up the default type evaluator. final Object target = getTarget(); if (target != null) { final int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].setupSetterAndGetter(target); } } super.initAnimation(); } } } ``` #### setupSetterAndGetter ``` public final class ObjectAnimator extends ValueAnimator { /***部分代码省略***/ void setupSetterAndGetter(Object target) { mKeyframes.invalidateCache(); if (mProperty != null) { /***部分代码省略***/ } // We can't just say 'else' here because the catch statement sets mProperty to null. if (mProperty == null) { Class targetClass = target.getClass(); if (mSetter == null) { //初始化mSetter setupSetter(targetClass); } /***部分代码省略***/ } } //初始化mSetter用于以后反射执行get、set操作 void setupSetter(Class targetClass) { Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType(); mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType); } } ``` ### animation #### ValueAnimator.doAnimationFrame ``` public class ValueAnimator extends Animator { /***部分代码省略***/ final boolean doAnimationFrame(long frameTime) { /***部分代码省略***/ return animationFrame(currentTime); } boolean animationFrame(long currentTime) { boolean done = false; switch (mPlayingState) { case RUNNING: case SEEKED: /***部分代码省略***/ if (fraction >= 1f) { //mCurrentIteration是否等于mRepeatCount if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) { // Time to repeat /***部分代码省略***/ } else { //执行完这次,该动画结束 done = true; fraction = Math.min(fraction, 1.0f); } } if (mPlayingBackwards) { fraction = 1f - fraction; } //设置View的属性值 animateValue(fraction); break; } return done; } } ``` #### ValueAnimator.animateValue ``` public class ValueAnimator extends Animator { /***部分代码省略***/ void animateValue(float fraction) { fraction = mInterpolator.getInterpolation(fraction); mCurrentFraction = fraction; int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { //PropertyValuesHolder.calculateValue就是计算每帧动画所对应的值 mValues[i].calculateValue(fraction); } if (mUpdateListeners != null) { int numListeners = mUpdateListeners.size(); for (int i = 0; i < numListeners; ++i) { //属性值得改变的回调 mUpdateListeners.get(i).onAnimationUpdate(this); } } } } ``` #### ObjectAnimator.animateValue ``` public final class ObjectAnimator extends ValueAnimator { /***部分代码省略***/ @Override void animateValue(float fraction) { final Object target = getTarget(); if (mTarget != null && target == null) { // We lost the target reference, cancel and clean up. cancel(); return; } //ValueAnimator.animateValue方法 super.animateValue(fraction); int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { //设置target的属性值,进行View的移动,产生动画 mValues[i].setAnimatedValue(target); } } } ``` ## PropertyValuesHolder > PropertyValuesHolder这个类的意义就是,它其中保存了动画过程中所需要操作的属性和对应的值。我们通过ofFloat(Object target, String propertyName, float… values)构造的动画,ofFloat()的内部实现其实就是将传进来的参数封装成PropertyValuesHolder实例来保存动画状态。在封装成PropertyValuesHolder实例以后,后期的各种操作也是以PropertyValuesHolder为主的。 ### ObjectAnimator.ofFloat 我们先看看我们之前的代码中构造ObjectAnimator的方法: ``` public final class ObjectAnimator extends ValueAnimator { /***部分代码省略***/ private ObjectAnimator(Object target, String propertyName) { setTarget(target); setPropertyName(propertyName); } public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) { //构造ObjectAnimator ObjectAnimator anim = new ObjectAnimator(target, propertyName); anim.setFloatValues(values); return anim; } //设置属性值 public void setPropertyName(@NonNull String propertyName) { /***部分代码省略***/ mPropertyName = propertyName; // New property/values/target should cause re-initialization prior to starting mInitialized = false; } @Override public void setFloatValues(float... 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.ofFloat(mProperty, values)); } else { setValues(PropertyValuesHolder.ofFloat(mPropertyName, values)); } } else { super.setFloatValues(values); } } } ``` ### 构造FloatPropertyValueHolder ``` public class PropertyValuesHolder implements Cloneable { /***部分代码省略***/ public static PropertyValuesHolder ofFloat(String propertyName, float... values) { return new FloatPropertyValuesHolder(propertyName, values); } } ``` ``` public class PropertyValuesHolder implements Cloneable { /***部分代码省略***/ static class FloatPropertyValuesHolder extends PropertyValuesHolder { public FloatPropertyValuesHolder(String propertyName, float... values) { super(propertyName); setFloatValues(values); } /***部分代码省略***/ @Override public void setFloatValues(float... values) { super.setFloatValues(values); mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes; } @Override void setAnimatedValue(Object target) { /***部分代码省略***/ if (mSetter != null) { try { mTmpValueArray[0] = mFloatAnimatedValue; //反射操作target的属性,通过set、get方法 mSetter.invoke(target, mTmpValueArray); } catch (InvocationTargetException e) { Log.e("PropertyValuesHolder", e.toString()); } catch (IllegalAccessException e) { Log.e("PropertyValuesHolder", e.toString()); } } } } } ``` ## 属性动画的内存泄露 - 上面讲述到 `ValueAnimator.AnimationHandler.doAnimationFrame` 的时候说过,这个方法会循环执行。 - 因为 `ValueAnimator.AnimationHandler.doAnimationFrame` 每次执行完动画(如果动画没有结束),都在再一次请求Vsync同步信号回调给自己。 - `Choreographer` 的回调都配post进入了当前线程的looper队列中。 - `mRepeatCount` 无穷大,会导致该循环会一直执行下去,即使关闭当前的页面也不会停止。