属性动画源码探究
来源:互联网 发布:我的淘宝网首页登录 编辑:程序博客网 时间:2024/06/07 15:06
在Anroid 3.0之前,Android系统给我们提供了两种动画效果方式:补间动画和帧动画,3.0之后有了属性动画,属性动画与前两者的区别在于属性动画是改变的View的实际属性。比如我们改变一个Button的位置,用属性动画平移后,View是可以响应点击事件的。而用前面两种动画则不行。
属性动画我们经常用到的是ObjectAnimator.ofFloat方法,今天我们就从这个方法入手看看源码:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) { ObjectAnimator anim = new ObjectAnimator(target, propertyName); anim.setFloatValues(values); return anim; }
首先是新建一个ObjectAnimator实例,讲我们传进来的view和要操作的属性名保存为ObjectAnimator的全局变量。然后是对我们传入的values进行层层封装。
看看anim.setFloatValues方法的实现。
@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); } }
mValues是PropertyValuesHolder[] 数组,mProperty是Property类的实例,它们都还没有被初始化,所以都为null,所以下面会调用setValues(PropertyValuesHolder.ofFloat(mPropertyName, 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; }
这个方法的最终目的就是给将PropertyValuesHolder存入到mValuesMap里面,以我们传入的属性名作为key,而我们传入的values这时已经被封装到了PropertyValuesHolder里面,是通过PropertyValuesHolder.ofFloat(mPropertyName, values)这行代码进行封装的,进入PropertyValuesHolder的ofFloat方法,
public static PropertyValuesHolder ofFloat(String propertyName, float... values) { return new FloatPropertyValuesHolder(propertyName, values); }
发现新建了一个FloatPropertyValuesHolder,将我们属性名mPropertyName和Values传了进去。很明显FloatPropertyValuesHolder是PropertyValuesHolder的子类。
public FloatPropertyValuesHolder(String propertyName, float... values) { super(propertyName); setFloatValues(values); }
在构造方法里接着调用了setFloatValues方法,
@Override public void setFloatValues(float... values) { super.setFloatValues(values); mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes; }
接着会调用父类的setFloatValues方法
public void setFloatValues(float... values) { mValueType = float.class; mKeyframes = KeyframeSet.ofFloat(values); }
看到这里好熟悉的套路啊 又跳出来个KeyframeSet的ofFloat方法
public static KeyframeSet ofFloat(float... values) { boolean badValue = false; int numKeyframes = values.length; FloatKeyframe keyframes[] = new FloatKeyframe[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 (int i = 1; i < numKeyframes; ++i) { keyframes[i] = (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]); if (Float.isNaN(values[i])) { badValue = true; } } } if (badValue) { Log.w("Animator", "Bad value (NaN) in float animator"); } return new FloatKeyframeSet(keyframes); }
这段代码很好理解,先创建了一个FloatKeyframe类型的keyframes[] 数组,接着看下面的for循环里,讲values通过Keyframe.ofFloat方法封装到了FloatKeyframe里面,然后添加到keyframes[] 数组里。最后将keyframes数组作为参数传到FloatKeyframeSet里。
回过头来再想一想,上面的这些就是对我们通过ObjectAnimator.ofFloat()方法传入的values进行了层层封装,最后封装到了FloatKeyframe这个关键帧里面,FloatKeyframeSet(的父类KeyframeSet)持有FloatKeyframe的引用,PropertyValuesHolder持有KeyframeSet的引用,而PropertyValueHolder又被存入到了ObjectAnimator(的父类ValueAnimator)的mValuesMap一个map里。
一些初始化的工作完成后,我们就会调用start方法开始执行动画,那么我们看看ObjectAnimator的start方法,调用了父类(ValueAnimator)的start方法,
private void start(boolean playBackwards) { if (Looper.myLooper() == null) { throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } mReversing = playBackwards; // Special case: reversing from seek-to-0 should act as if not seeked at all. if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) { if (mRepeatCount == INFINITE) { // Calculate the fraction of the current iteration. float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction)); mSeekFraction = 1 - fraction; } else { mSeekFraction = 1 + mRepeatCount - mSeekFraction; } } mStarted = true; mPaused = false; mRunning = false; mAnimationEndRequested = false; // Resets mLastFrameTime when start() is called, so that if the animation was running, // calling start() would put the animation in the // started-but-not-yet-reached-the-first-frame phase. mLastFrameTime = 0; AnimationHandler animationHandler = AnimationHandler.getInstance(); animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale)); if (mStartDelay == 0 || mSeekFraction >= 0) { // If there's no start delay, init the animation and notify start listeners right away // to be consistent with the previous behavior. Otherwise, postpone this until the first // frame after the start delay. startAnimation(); if (mSeekFraction == -1) { // No seek, start at play time 0. Note that the reason we are not using fraction 0 // is because for animations with 0 duration, we want to be consistent with pre-N // behavior: skip to the final value immediately. setCurrentPlayTime(0); } else { setCurrentFraction(mSeekFraction); } } }
有这么两行代码
AnimationHandler animationHandler = AnimationHandler.getInstance();
animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
执行到了这里会有一个callback回调,传入的参数是this,也就是说ValueAnimator类肯定实现了这个callback,我们去看一下 确实是实现了AnimationHandler.AnimationFrameCallback接口,那么找它的实现方法doAnimationFrame
public final void doAnimationFrame(long frameTime) { AnimationHandler handler = AnimationHandler.getInstance(); if (mLastFrameTime == 0) { // First frame handler.addOneShotCommitCallback(this); if (mStartDelay > 0) { startAnimation(); } if (mSeekFraction < 0) { mStartTime = frameTime; } else { long seekTime = (long) (getScaledDuration() * mSeekFraction); mStartTime = frameTime - seekTime; mSeekFraction = -1; } mStartTimeCommitted = false; // allow start time to be compensated for jank } mLastFrameTime = frameTime; if (mPaused) { mPauseTime = frameTime; handler.removeCallback(this); return; } 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 } handler.addOneShotCommitCallback(this); } // 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); boolean finished = animateBasedOnTime(currentTime); if (finished) { endAnimation(); } }
这里首先会执行startAnimation()方法,后面还会执行animateBasedOnTime()方法,
线点进去看看startAnimation()方法,
private void startAnimation() { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(), System.identityHashCode(this)); } mAnimationEndRequested = false; initAnimation(); mRunning = true; if (mSeekFraction >= 0) { mOverallFraction = mSeekFraction; } else { mOverallFraction = 0f; } if (mListeners != null) { notifyStartListeners(); } }
我们看到有个initAnimation()方法,注意这里有个坑,按cmd键点进去这是ValueAninmator的initAnimation()方法,暂且先看一下这个方法:
@CallSuper void initAnimation() { if (!mInitialized) { int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].init(); } mInitialized = true; } }
调用了PropertyValueHolder的init()方法。
void init() { if (mEvaluator == null) { // We already handle int and float automatically, but not their Object // equivalents mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : (mValueType == Float.class) ? sFloatEvaluator : null; } if (mEvaluator != null) { // KeyframeSet knows how to evaluate the common types - only give it a custom // evaluator if one has been set on this class mKeyframes.setEvaluator(mEvaluator); } }
我们的估值器TypeEvaluator就是在PropertyValueHolder的init方法里被设置进去的。
然而不应该执行到就结束了啊 我们还没真正的找到动画是怎么执行的呢!!执行动画起码应该是对View做点什么吧。。。我们传进来的参数被封装了也没看到怎么用的呢?
所以我们找错代码了… 坑就在上面刚说的initAnimation()这个方法。父类ValueAnimator有这个方法,但是子类ObjectAnimator也有这个方法。
我们进入ObjectAnimator的initAnimation()方法里看看 ,。
@CallSuper @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(); } }
for循环里面调用了PropertyValueHolder的setupSetterAndGetter(target)方法,找到这个方法target就是我们传入的view,而看这个方法名里有setter和getter,我们猜可能是会调用view的set和get方法。其实我理解这个方法的最终目的就是给PropertyValueHolder的两个成员变量mSetter和mGetter赋值的。这两个都是Method的实例,如果我们想通过反射去调用view的setXXX()和getXXX()方法,我们就要拿到Method,然后调用Method的invoke()方法。当然我们还需要知道方法名,比如View.setTranslationX()方法。我们使用ObjectAnimator.ofFloat(view,”translationX”,values),这里传入的translationX就是用来获取方法名的,源码里是通过getMethodName方法来获取的实现方式如下:
String methodName = getMethodName("set", mPropertyName);static String getMethodName(String prefix, String propertyName) { if (propertyName == null || propertyName.length() == 0) { // shouldn't get here return prefix; } char firstLetter = Character.toUpperCase(propertyName.charAt(0)); String theRest = propertyName.substring(1); return prefix + firstLetter + theRest; }
如果想获取set方法的方法名 第一个参数传入set,第二个参数就是我们的translationX, 它会截取这个字符串的第一个字符,然后设置成大写,也就是firstLetter等于大写T, 而theRest就是剩下的ranslationX,然后和prefix进行拼接 成 setTranslationX, 因此我们在写这个属性的时候搞不清首字母是大写还是小写,看到这里你就明白了 ,都可以哈。。
写到这里,这个分支是从上面的doAnimationFrame()方法里的startAnimation()方法进入顺下来的,下面再来看看另一个分支,就是在doAnimationFrame方法里startAnimation方法执行完后下面还有一个方法是animateBasedOnTime()方法,开始进入,看主要代码执行了animateValue(currentIterationFraction)方法,而这个方法也是一个坑,就是父类和子类都有这个方法,找代码的时候容易迷失方向。
看子类ObjectAnimator的animateValue方法
@CallSuper @Override void animateValue(float fraction) { final Object target = getTarget(); if (mTarget != null && target == null) { // We lost the target reference, cancel and clean up. Note: we allow null target if the /// target has never been set. cancel(); return; } super.animateValue(fraction); int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].setAnimatedValue(target); } }
先是调用了父类的super.animateValue方法,然后又调用了PropertyValueHolder的setAnimatedValue方法。
先从父类的super.animateValue看起。
@CallSuper 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); } } }
通过插值器Interpolator计算得到fraction,然后for循环调用PropertyValueHolder的calculateValue方法,计算得到的值赋值给PropertyValueHolder的变量mFloatAnimatedValue,就是动画执行到某一时刻的值。然后进入下面的for循环 mUpdateListeners.get(i).onAnimationUpdate(this);回调。这是父类(ValueAnimator)的animateValue方法执行的事情,再回到子类(ObjectAnimator)的animateValue方法,下面有一个for循环,调用了PropertyValueHolder的setAnimatedValue方法
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()); } } }
mSetter.invoke(target, mTmpValueArray);
这个方法最后的调用才让我们的view执行了动画。
所以属性的动画的原理就是通过反射来改变view的属性从而让view动起来的。
结束
- 属性动画源码探究
- 属性动画源码分析
- 属性动画源码分析
- runtime源码探究(三)属性关联
- 属性动画ValueAnimator源码解析
- 安卓属性动画源码解析
- Android属性动画ValueAnimator源码简单分析
- Android属性动画ObjectAnimator源码简单分析
- Android属性动画AnimatorSet源码简单分析
- Android属性动画ObjectAnimator源码简单分析
- 属性动画学习ValueAnimator---------源码阅读
- 属性动画(ObjectAnimator)源码分析
- Android动画详细探究
- Android动画详细探究
- Android动画探究
- 核心动画探究
- Core Animation 显式动画属性值探究(完善中......)
- Android 属性动画探究(一)——Interpolator解析与自定义
- hbase的表与phoenix建立映射
- Failed to run the WC DB work queue associated with 错误的解决
- @Controller和@RestController的区别?
- 简单选择排序
- 使用sublime文件夹中全局查找文件内容
- 属性动画源码探究
- ReactNative TextInput 组件
- memcache启动、关闭参数
- 艾伦·麦席森·图灵
- 最长公共子序列 与 最长公共子串
- LabVIEW的引用调用异步调用和子VI的区别
- Hadoop基础教程-第7章 MapReduce进阶(7.6 MapReduce 二次排序)
- echarts 移动端 滑动 不好用
- Oracle中的列转行函数listagg()