(二十二)Animator 源码分析
来源:互联网 发布:达尔朗 知乎 编辑:程序博客网 时间:2024/05/19 02:40
版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
这是是根据 API 25 进行 ObjectAnimator 的使用代码分析。
public void startAnimator(View view){ ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f); objectAnimator.setDuration(500); objectAnimator.start(); }
一、ofFloat
ObjectAnimator 的 ofFloat:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) { ObjectAnimator anim = new ObjectAnimator(target, propertyName); anim.setFloatValues(values); return anim; }
在这里,他先 new 了一个 ObjectAnimator,跟进去查看一下:调用了 setTarget 和 setPropertyName 方法。
private ObjectAnimator(Object target, String propertyName) { setTarget(target); setPropertyName(propertyName); }
很明显,setTarget 设置的是改属性动画的执行对象是谁,setPropertyName 设置要执行的是什么属性。
ObjectAnimator 的 setTarget:
public void setTarget(@Nullable Object target) { final Object oldTarget = getTarget(); if (oldTarget != target) { if (isStarted()) { cancel(); } mTarget = target == null ? null : new WeakReference<Object>(target); // New target should cause re-initialization prior to starting mInitialized = false; } }
在 setTarget 中把要执行动画的对象存在成员变量 mTarget 中,这边采用的是弱引用,可以有效的避免内存泄漏。同样的,在 setPropertyName 中把执行动画的属性存在成员变量 mProperty 中。
继续 ofFloat 往下,调用了 ObjectAnimator 的 setFloatValues 方法。
ObjectAnimator 的 setFloatValues:
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 是不会为空的,走 else,调用 ValueAnimator 的 setFloatValues 方法。
ValueAnimator 的 setFloatValues:
public void setFloatValues(float... values) { if (values == null || values.length == 0) { return; } if (mValues == null || mValues.length == 0) { setValues(PropertyValuesHolder.ofFloat("", values)); } else { PropertyValuesHolder valuesHolder = mValues[0]; valuesHolder.setFloatValues(values); } // New property/values/target should cause re-initialization prior to starting mInitialized = false; }
在 ValueAnimator 的 setFloatValues 中,使用了一个 PropertyValuesHolder 这个类,第一次调用 mValues 是空的,调用 PropertyValuesHolder.ofFloat(“”, values)。一直点下去,最终也跟 else 一样调用了 PropertyValuesHolder 的 setFloatValues 方法。
PropertyValuesHolder 的 setFloatValues:
public void setFloatValues(float... values) { //设置值类型 mValueType = float.class; //设置关键帧 mKeyframes = KeyframeSet.ofFloat(values); }
直接设置值类型 mValueType 为 float 类型,可以猜想,如果是 setIntValues ,那值类型 mValueType 设置为 int。
mKeyframes 是存放关键帧,关键帧在初始时候传入属性值变化的范围(下放代码 0f 和 200f 即关键帧)
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f);
当传入多个的时候,关键帧也将变成多个。下方关键帧有 4 个,变化区间有三个,如果动画执行时间是 300ms,则每个区间的执行时间将均匀分配为 100ms。(为什么平均分配,后面看到再提)
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f, 500f, 400f);
KeyframeSet 的 ofFloat:
public static KeyframeSet ofFloat(float... values) { boolean badValue = false; int numKeyframes = values.length; //FloatKeyframe 是一个接口,在这 new 了一个 FloatKeyframe 数组 //关键帧只有一个的时候,默认加一个 0f 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) { //(float) i / (numKeyframes - 1) 则是为了将时间均分 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); }
在这边,判断关键帧的个数,当关键帧只有一个的时候,添加一个 0f 的关键帧,所以,下面两行代码效果是一致的:
ObjectAnimator.ofFloat(view,"translationX", 1f);ObjectAnimator.ofFloat(view,"translationX", 0f, 1f);
中间先不管,最中是 反悔了一个 new FloatKeyframeSet(keyframes),在这里直接调用 super(keyframes), FloatKeyframeSet 的父类是 KeyframeSet ,即又调回到 KeyframeSet 的构造方法。
KeyframeSet 的构造方法:
public KeyframeSet(Keyframe... keyframes) { mNumKeyframes = keyframes.length; // immutable list //保存所有帧 mKeyframes = Arrays.asList(keyframes); //设置第一帧 mFirstKeyframe = keyframes[0]; //设置最后一帧 mLastKeyframe = keyframes[mNumKeyframes - 1]; mInterpolator = mLastKeyframe.getInterpolator(); }
顺便查看一下 KeyframeSet 的其他成员变量,可以发现,KeyframeSet 还保存有估值器和插值器。
总结: ObjectAnimator 的 ofFloat 方法主要做一些初始化操作,新建了一个 ObjectAnimator 对象,保存了要执行动画的对象和属性,并对关键帧进行了解析。
二、start
1.回调
接下来继续看一下 ObjectAnimator 的 start 方法。
ObjectAnimator 的 start:
public void start() { AnimationHandler.getInstance().autoCancelBasedOn(this); 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(); }
中间依稀处理忽略,最终调用了 super.start(),ObjectAnimator 的父类是 ObjectAnimator,即调用了 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,通过广播的机制由底层进行回调,每 16ms 进行一次回调。(再下去就比较底层了,涉及到垂直刷新,不继续了)
继续查看 AnimationHandler 的 addAnimationFrameCallback 方法。
AnimationHandler 的 addAnimationFrameCallback:
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) { if (mAnimationCallbacks.size() == 0) { //再分析下去比较复杂,总之广播接收到会调用 mFrameCallback。 getProvider().postFrameCallback(mFrameCallback); } //保存回调 if (!mAnimationCallbacks.contains(callback)) { mAnimationCallbacks.add(callback); } if (delay > 0) { mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay)); } }
我们看一下变量 mFrameCallback,回调的时候调用了 doAnimationFrame 方法。getProvider().getFrameTime() 获取的当前动画执行的真正时间。
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) { doAnimationFrame(getProvider().getFrameTime()); if (mAnimationCallbacks.size() > 0) { getProvider().postFrameCallback(this); } } };
AnimationHandler 的 doAnimationFrame:
private void doAnimationFrame(long frameTime) { int size = mAnimationCallbacks.size(); long currentTime = SystemClock.uptimeMillis(); for (int i = 0; i < size; i++) { final AnimationFrameCallback callback = mAnimationCallbacks.get(i); if (callback == null) { continue; } if (isCallbackDue(callback, currentTime)) { //循环遍历调用所有回调 callback.doAnimationFrame(frameTime); if (mCommitCallbacks.contains(callback)) { getProvider().postCommitCallback(new Runnable() { @Override public void run() { commitAnimationFrame(callback, getProvider().getFrameTime()); } }); } } } cleanUpList(); }
这里的 callback 是由 ValueAnimator 中传递过来的 this,即 ValueAnimator 本身。ValueAnimator 的 doAnimationFrame 方法会调用 startAnimation 方法,跟 ObjectAnimator 的 start 一样。
2.反射方法
继续 ValueAnimator 的 start 方法,接下去直接调用 startAnimation() 启动动画,
ValueAnimator 的 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() 进行一些初始化操作,这边要注意一点的是,我们在这边分析的 ObjectAnimator 的源码,ObjectAnimator 有对 initAnimation 方法进行重写。
ObjectAnimator 的 initAnimation :
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 是父类 ValueAnimator 的属性,是 PropertyValuesHolder mValues[i].setupSetterAndGetter(target); } } super.initAnimation(); } }
ObjectAnimator 的 initAnimation 调用了 PropertyValuesHolder 的 setupSetterAndGetter 方法。
PropertyValuesHolder 的 setupSetterAndGetter:
void setupSetterAndGetter(Object target) { ... Class targetClass = target.getClass(); if (mSetter == null) { setupSetter(targetClass); } ... }
PropertyValuesHolder 的 setupSetter:
void setupSetter(Class targetClass) { Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType(); //传入 set,反射生成 set 方法。 mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType); }
PropertyValuesHolder 的 setupSetterOrGetter:
private Method setupSetterOrGetter(Class targetClass, HashMap<Class, HashMap<String, Method>> propertyMapMap, String prefix, Class valueType) { ... setterOrGetter = getPropertyFunction(targetClass, prefix, valueType); ... } return setterOrGetter; }
PropertyValuesHolder 的 getPropertyFunction:
private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { // TODO: faster implementation... Method returnVal = null; String methodName = getMethodName(prefix, mPropertyName); Class args[] = null; if (valueType == null) { try { returnVal = targetClass.getMethod(methodName, args); } catch (NoSuchMethodException e) { // Swallow the error, log it later } } else { ... } return returnVal; }
PropertyValuesHolder 的 getPropertyFunction 最终通过反射生产对应的 set 或 get 方法并返回。
在这边顺便提一下 getMethodName 方法。
PropertyValuesHolder 的 getMethodName:
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; }
getMethodName 方法对传入的方法名进行首字母修改为大写处理,所以在传属性的时候,首字母大小写是没有影响的。下方两行代码效果一样:
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f); ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "TranslationX", 0f, 200f);
ObjectAnimator 的 initAnimation 在最后调用了 super.initAnimation(),即 ValueAnimator 的 initAnimation 方法。
ValueAnimator 的 initAnimation :
void initAnimation() { if (!mInitialized) { int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].init(); } mInitialized = true; } }
mValues[i].init() 调用了 PropertyValuesHolder 的 init,在这里进行了估值器的设置。
总结: 到这里,PropertyValuesHolder 的成员变量 mSetter 为对应属性的 set 方法。
3.方法的调用
再次回到 ValueAnimator 的 start 继续向下。
ValueAnimator 的 start :
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); }
ValueAnimator 的 setCurrentPlayTime :
public void setCurrentPlayTime(long playTime) { float fraction = mDuration > 0 ? (float) playTime / mDuration : 1; setCurrentFraction(fraction); }
所以,ObjectAnimator 的 start 最终调用到了 setCurrentFraction 方法,传入的是动画执行的的百分比。在 setCurrentFraction 方法里面调用了 animateValue 方法。注意,ValueAnimator 也重写了该方法。
ObjectAnimator 的 animateValue :
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); } }
ObjectAnimator 的 animateValue 调用了 super.animateValue(fraction), 在这里进行了插值器的使用。
最后调用了 mValues[i].setAnimatedValue(target),即 PropertyValuesHolder 的 setAnimatedValue 方法。
PropertyValuesHolder 的 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()); } } }
在这里进行前面反射的方法的调用,即我们设置的属性的 set 方法的调用,每次底层回调都会进行一次重新调用改属性的 set 方法进行设置,从而达到动画的效果。
- (二十二)Animator 源码分析
- Android Animator 源码分析
- Redis源码分析(二十二)--- networking网络协议传输
- (二十)Animator 基础
- Redis源码分析(二十二)——CRC循环冗余校验
- mongodb源码分析(二十二)mongos chunk的拆分
- Animator源码
- appium 源码分析(十二)-pressKeyCode,LongPressKeyCode
- Shark源码分析(十二):线性SVM
- Android动画机制学习---animator(二)
- Elasticsearch源码分析十二--过滤器
- Minetest源码分析十二:ServerMap
- three.js 源码注释(二十二)Core/Object3D.js
- Android源码解析(二十二)-->Toast加载绘制流程
- Redis源码分析(十二)——列表类型t_list
- spark源码学习(十二)--- checkpoint机制分析
- jQuery-1.9.1源码分析系列(十二) 筛选操作
- scrapy源码分析(十二)---------下载中间件RobotsTxtMiddleware
- 【密码学】C语言实现AES核心步骤
- LeetCode-矩阵旋转
- 1064. 朋友数(20)
- RAID--独立冗余磁盘阵列
- Mapping Solr Converter
- (二十二)Animator 源码分析
- Codeforces Round #363 (Div. 2) C Vacations
- 回顾大一·C语言编程11.4(3)(4)+12.1(1)+12.1(2)①②+实验12.1(3)+12.2+12.3
- 丢失libiconv-2.dll解决办法以及无法定位输入点libiconv-2.dll到动态链接库
- git 到gitub中出现的问题
- CCS7.2软/硬件断点设置,精确计算代码运行时间及定时器定时时间验证(适用于C28x芯片)
- mysql 加载本地数据文件
- QEMU 运行ARM Linux Kernel
- Python学习(十四)——面向对象