在看本文之前,可以先阅读以下官方文档关于Property Animation的一些介绍和用法,地址为http://developer.android.com/guide/topics/graphics/prop-animation.html
那么下面就来讲一下ValueAnimator和ObjectAnimator的工作原理。
1.ValueAnimator
ValueAnimator可以说是整个属性动画框架的核心类,动画的驱动就是在此类中实现的。下面举一个ValueAnimator的实例:
ValueAnimator anim= ValueAnimator.ofInt(0, 40); animation.setDuration(40); animation.start();
然后通过ValueAnimator的AnimatorUpdateListener可以得到动画执行每一帧所返回的值,
anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //frameValue是通过startValue和endValue以及Fraction计算出来的 int frameValue = (Integer)animation.getAnimatedValue(); //利用每一帧返回的值,可以做一些改变View大小,坐标,透明度等等操作 } });
究竟ValueAnimator是怎么工作的呢,让我根据以上代码一句一句的进行分析,首先来看ValueAnimator.ofInt(0, 40);
//ofInt或ofFloat提供的values最终都被包装成一个PropertyValuesHolder //不同于ObjectAnimator这里提供的值必须两个以上,ObjectAnimator可以只提供一个endValue public static ValueAnimator ofInt(int... values) { ValueAnimator anim = new ValueAnimator(); anim.setIntValues(values); return anim; } /** * 通过以下代码可以看出,PropertyValuesHolder以某种方式保存了values的值。 * 在ValueAnimator中操作的都是valuesHolder对象 * @param values */ public void setIntValues(int... values) { if (values == null || values.length == 0) { return; } if (mValues == null || mValues.length == 0) { //这里PropertyValuesHolder.ofInt提供的字符串参数为“”,是因为ValueAnimator并不提供目标对象,自然无法提供属性名称 setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofInt("", values)}); } else { //这里是为了处理重复设置 PropertyValuesHolder valuesHolder = mValues[0]; valuesHolder.setIntValues(values); } // New property/values/target should cause re-initialization prior to starting mInitialized = false; } //把PropertyValuesHolder对象保存起来 public void setValues(PropertyValuesHolder... values) { int numValues = values.length; mValues = values; mInitialized = false; }
下面再来看一下PropertyValuesHolder中是以什么方式来保存提供的values值的
/** * IntPropertyValuesHolder继承自PropertyValuesHolder ,用来封装int类型的值 **/ public static PropertyValuesHolder ofInt(String propertyName, int... values) { return new IntPropertyValuesHolder(propertyName, values); }
来看IntPropertyValuesHolder类
public IntPropertyValuesHolder(String propertyName, int... values) { super(propertyName); setIntValues(values); } /** * 这里会发现调用了父类的setIntValues方法, * 并且产生了一个mKeyframeSet对象 * @param values */ @Override public void setIntValues(int... values) { super.setIntValues(values); mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; }
再回到PropertyValueHolder查看实现
/** * mValueType作用是确定ObjectAnimator执行set方法时 * 的参数类型 * 而values被包装成了一个KeyframeSet * @param values */ public void setIntValues(int... values) { mValueType = int.class; mKeyframeSet = KeyframeSet.ofInt(values); }
再进入KeyframeSet查看KeyframeSet.ofInt(values)具体实现
/** * 从此方法可以看到,values数组中的每一个值都被包装成了IntKeyframe, * IntKeyframe保存一个当前值的fraction,value和mValueType。 * IntKeyframeSet则保存是一组IntKeyframe的集合。 * @param values * @return */ public static KeyframeSet ofInt(int... values) { int numKeyframes = values.length; IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)]; if (numKeyframes == 1) { //这里只可能是ObjectAnimator才会发生, keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f); //这里value为0,默认从ObjectAnimator中的目标对象中得到 keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]); } else { keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]); for (int i = 1; i < numKeyframes; ++i) { //根据值个数拆分 keyframes[i] = (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]); } } return new IntKeyframeSet(keyframes); }
从这里可以看到,当初始化ValueAnimator时,同时也把传入的每一个数据值包装成了一个Keyframe,
并保存在KeyframeSet中,而KeyframeSet则是在PropertyValueHolder初始化的。
初始化工作完成了,然后通过animation.setDuration(40);设置duration的值,这个非常重要。
然后通过anim.start(),开始执行动画
/** *所有执行了Start方法的ValueAnimator都会被临时保存在sAnimations */ private static final ThreadLocal<ArrayList<ValueAnimator>> sPendingAnimations = new ThreadLocal<ArrayList<ValueAnimator>>() { @Override protected ArrayList<ValueAnimator> initialValue() { return new ArrayList<ValueAnimator>(); } }; /** * * @param playBackwards Whether the ValueAnimator should start playing in reverse. * */ private void start(boolean playBackwards) { /** * 确保start方法是在UI线程中执行,因为UI线程在ActivityThread中 * 会初始化一个Looper */ if (Looper.myLooper() == null) { throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } mPlayingBackwards = playBackwards; mCurrentIteration = 0; mPlayingState = STOPPED; //动画初始执行状态 mStarted = true; mStartedDelay = false; sPendingAnimations.get().add(this); //所有执行start方法的ValueAnimator都会被临时保存到此集合 if (mStartDelay == 0) { // This sets the initial value of the animation, prior to actually starting it running setCurrentPlayTime(getCurrentPlayTime()); mPlayingState = STOPPED; mRunning = true; if (mListeners != null) { ArrayList<AnimatorListener> tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone(); int numListeners = tmpListeners.size(); for (int i = 0; i < numListeners; ++i) { //告诉注册的了这个动画的所有监听,动画已经开始运行 tmpListeners.get(i).onAnimationStart(this); } } } AnimationHandler animationHandler = sAnimationHandler.get(); if (animationHandler == null) { //初始化唯一一个animationHandler animationHandler = new AnimationHandler(); sAnimationHandler.set(animationHandler); } animationHandler.sendEmptyMessage(ANIMATION_START); } @Override public void start() { start(false); }
属性动画逐帧更新都是由handler不断发生消息来实现的,下面就来看一下 AnimationHandler的实现
/** * 马上就开始执行的动画集合 */ private static final ThreadLocal<ArrayList<ValueAnimator>> sAnimations = new ThreadLocal<ArrayList<ValueAnimator>>() { @Override protected ArrayList<ValueAnimator> initialValue() { return new ArrayList<ValueAnimator>(); } }; /** * 需要延时执行的所有动画都会被临时保存到sDelayedAnims */ private static final ThreadLocal<ArrayList<ValueAnimator>> sDelayedAnims = new ThreadLocal<ArrayList<ValueAnimator>>() { @Override protected ArrayList<ValueAnimator> initialValue() { return new ArrayList<ValueAnimator>(); } }; /** * 执行结束的所有动画都会被临时保存到sEndingAnims */ private static final ThreadLocal<ArrayList<ValueAnimator>> sEndingAnims = new ThreadLocal<ArrayList<ValueAnimator>>() { @Override protected ArrayList<ValueAnimator> initialValue() { return new ArrayList<ValueAnimator>(); } }; /** * * 用来驱动动画执行的handler, * 所有的属性动画都会用同一个handler来处理。 * 此handler主要处理动画开始动作,以及动画的每一帧 */ private static class AnimationHandler extends Handler { @Override public void handleMessage(Message msg) { boolean callAgain = true; //已经准备开始执行的动画 ArrayList<ValueAnimator> animations = sAnimations.get(); //需要延迟执行的动画 ArrayList<ValueAnimator> delayedAnims = sDelayedAnims.get(); switch (msg.what) { //start方法执行后,会发送消息执行ANIMATION_START中的代码 case ANIMATION_START: ArrayList<ValueAnimator> pendingAnimations = sPendingAnimations.get(); if (animations.size() > 0 || delayedAnims.size() > 0) { callAgain = false; } /** * 由于一个动画的开始可能触发另外一个动画,意味着可能有新的动画被添加进pendingAnimations, * 所以这里不断遍历直到pendingAnimations为空 */ while (pendingAnimations.size() > 0) { ArrayList<ValueAnimator> pendingCopy = (ArrayList<ValueAnimator>) pendingAnimations.clone(); pendingAnimations.clear(); int count = pendingCopy.size(); for (int i = 0; i < count; ++i) { ValueAnimator anim = pendingCopy.get(i); //如果动画没有延迟时间,则把动画添加进入sAnimations,意味着动画即将执行 if (anim.mStartDelay == 0) { anim.startAnimation(); } else { //如果动画有一个延迟执行时间,则把动画添加进延迟集合delayedAnims delayedAnims.add(anim); } } } // 注意,这里没有break,那么ANIMATION_FRAME中代码将继续执行 case ANIMATION_FRAME: //获取当前时间,通过减去startTime,计算差值 long currentTime = AnimationUtils.currentAnimationTimeMillis(); ArrayList<ValueAnimator> readyAnims = sReadyAnims.get(); ArrayList<ValueAnimator> endingAnims = sEndingAnims.get(); //这里检查delayedAnims中动画是否可以被执行,我们可以不管这部分 int numDelayedAnims = delayedAnims.size(); for (int i = 0; i < numDelayedAnims; ++i) { ValueAnimator anim = delayedAnims.get(i); if (anim.delayedAnimationFrame(currentTime)) { readyAnims.add(anim); } } int numReadyAnims = readyAnims.size(); if (numReadyAnims > 0) { for (int i = 0; i < numReadyAnims; ++i) { ValueAnimator anim = readyAnims.get(i); anim.startAnimation(); anim.mRunning = true; delayedAnims.remove(anim); } readyAnims.clear(); } //接着看这里。 int numAnims = animations.size(); int i = 0; while (i < numAnims) { ValueAnimator anim = animations.get(i); //animationFrame方法返回true,意味着此动画已经执行完毕, //否则开始真正计算AnimatedValue的值,大家可以根据 //getAnimationValue来获取这个值,在ObjectAnimator中 //此方法还会不断设置object中的属性值 if (anim.animationFrame(currentTime)) { endingAnims.add(anim); } if (animations.size() == numAnims) { ++i; } else { //这里是处理当前正在执行的动画,被cancle的情况 // An animation might be canceled or ended by client code // during the animation frame. Check to see if this happened by // seeing whether the current index is the same as it was before // calling animationFrame(). Another approach would be to copy // animations to a temporary list and process that list instead, // but that entails garbage and processing overhead that would // be nice to avoid. --numAnims; endingAnims.remove(anim); } } //处理执行结束后的动画,结束后会把动画从所有集合中移除, //并且触发监听通知用户 if (endingAnims.size() > 0) { for (i = 0; i < endingAnims.size(); ++i) { endingAnims.get(i).endAnimation(); } endingAnims.clear(); } //如果任然有活动的动画或者延迟执行的动画,会继续执行下一帧 //执行下一帧的时间在0-10ms之间 if (callAgain && (!animations.isEmpty() || !delayedAnims.isEmpty())) { sendEmptyMessageDelayed(ANIMATION_FRAME, Math.max(0, sFrameDelay - (AnimationUtils.currentAnimationTimeMillis() - currentTime))); } break; } } }
在handler逐帧更新代码中,anim.animationFrame(currentTime),是计算动画执行过程中,值的变化的,
boolean animationFrame(long currentTime) { boolean done = false; switch (mPlayingState) { case RUNNING: case SEEKED: //这句代码是重点,通过currentTime和mStartTime的差值计算动画执行的进度,0-1的小数值 float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f; 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 ? false : true; } mCurrentIteration += (int)fraction; fraction = fraction % 1f; mStartTime += mDuration; } else { done = true; fraction = Math.min(fraction, 1.0f); } } //真正计算animationValue和执行onAnimationUpdate回调的 //方法在这里 animateValue(fraction); break; } return done; }
animationValue方法通过插值器重新计算一个fraction,
/** * * * @param fraction 通过总时间和已经执行时间,计算出来的动画进度 */ void animateValue(float fraction) { //通过插值器,对fraction重新计算,插值器不同计算出来的结果不同, //这个跟Tween动画时一样的 fraction = mInterpolator.getInterpolation(fraction); mCurrentFraction = fraction; int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].calculateValue(fraction); //真正计算animationValue的方法 } //没一帧的执行都会执行回调,这样使用者就能获取每一帧计算出来的animationValue了 if (mUpdateListeners != null) { int numListeners = mUpdateListeners.size(); for (int i = 0; i < numListeners; ++i) { mUpdateListeners.get(i).onAnimationUpdate(this); } } }
真正的计算是交给PropertyValuesHolder.calculateValue(float fraction)计算,之前我们看到传进来的values值已经被PropertyValuesHolder包装并保存起来了,那么现在我们就可以利用到这些值来进行计算了。
void calculateValue(float fraction) { mAnimatedValue = mKeyframeSet.getValue(fraction); }
查看IntKeyframeSet
public int getIntValue(float fraction) { if (mNumKeyframes == 2) { //当只有firstValue和lastValue时,发现最终是通过mEvaluator.evaluate来计算mAnimatedValue if (firstTime) { firstTime = false; firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue(); lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue(); deltaValue = lastValue - firstValue; } if (mInterpolator != null) { fraction = mInterpolator.getInterpolation(fraction); } //真正计算出mAnimatedValue的地方 if (mEvaluator == null) { return firstValue + (int)(fraction * deltaValue); } else { return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue(); } } //后面这些复制的操作可以先不看,是处理类似OvershootInterpolator插值器(超过给定值,再回来),和AnticipateInterpolator,先小于给定初始值,再正常继续执行,以及BounceInterpolator,弹性动作 if (fraction <= 0f) { final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0); final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(1); int prevValue = prevKeyframe.getIntValue(); int nextValue = nextKeyframe.getIntValue(); float prevFraction = prevKeyframe.getFraction(); float nextFraction = nextKeyframe.getFraction(); final TimeInterpolator interpolator = nextKeyframe.getInterpolator(); if (interpolator != null) { fraction = interpolator.getInterpolation(fraction); } float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); return mEvaluator == null ? prevValue + (int)(intervalFraction * (nextValue - prevValue)) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). intValue(); } else if (fraction >= 1f) { final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 2); final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 1); int prevValue = prevKeyframe.getIntValue(); int nextValue = nextKeyframe.getIntValue(); float prevFraction = prevKeyframe.getFraction(); float nextFraction = nextKeyframe.getFraction(); final TimeInterpolator interpolator = nextKeyframe.getInterpolator(); if (interpolator != null) { fraction = interpolator.getInterpolation(fraction); } float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); return mEvaluator == null ? prevValue + (int)(intervalFraction * (nextValue - prevValue)) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue(); } IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0); for (int i = 1; i < mNumKeyframes; ++i) { IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(i); if (fraction < nextKeyframe.getFraction()) { final TimeInterpolator interpolator = nextKeyframe.getInterpolator(); if (interpolator != null) { fraction = interpolator.getInterpolation(fraction); } float intervalFraction = (fraction - prevKeyframe.getFraction()) / (nextKeyframe.getFraction() - prevKeyframe.getFraction()); int prevValue = prevKeyframe.getIntValue(); int nextValue = nextKeyframe.getIntValue(); return mEvaluator == null ? prevValue + (int)(intervalFraction * (nextValue - prevValue)) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). intValue(); } prevKeyframe = nextKeyframe; } // shouldn't get here return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).intValue(); }
看类IntEvaluator
/** * 这里其实比较简单,通过startValue,endValue和fraction(动画执行的进度),计算出动画执行 *的中间值 **/ public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int startInt = startValue; return (int)(startInt + fraction * (endValue - startInt)); }
这样每一帧都会计算出当前进度的animationValue,而我们根据AnimatorUpdateListener就可以获取每一帧计算过后animationValue值了。
这样关于ValueAnimator工作的基本过程就都讲清楚了。下面看看ObjectAnimator。
2.ObjectAnimator
ObjectAnimator是通过传递进来一个对象,以及对象的属性名称,在anim.start()执行过程中不断更改对象的属性值,来实现动画效果的。
前提是传递进来的对象的属性,必须要具有相应的set和get方法。
ObjectAnimator是继承自ValueAnimator,前者的许多方法其实是调用ValueAnimator的方法,所以说ValueAnimator是动画核心,因为它处理了
驱动引擎这一块。ObjectAnimator多传入了两个参数,一个是target对象(可以是任何对象,不限于View),一个是对象属性propertyName(前提是所属对象必须拥有对应的setPropertyName(),getPropertyName方法)
下面还是来看一个ObjectAnimator的简单的例子:
ObjectAnimator anm = ObjectAnimator.ofFloat(myView, "alpha", 1); anm.setDuration(200); anm.start();
代码的作用是对View的alpha值进行更改,注意这里可以只提供一个value值,而ValueAnimator则必须提供两个值以上。这是因为:这里提供的一个值是作为
endValue,startValue则是通过myView.getAlpha()来获得。下面再来看看ObjectAnimator.ofFloat()方法:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) { ObjectAnimator anim = new ObjectAnimator(target, propertyName); anim.setFloatValues(values); return anim; }
注意看,这里实例化ObjectAnimator多传入了target对象和其对于的属性名称propertyName,这里的作用是在动画改变的过程中,不断通过对象的setPropertyName方法改变PropertyName的值,
在样在不断的重绘过程中获得新的值,就能实现动画效果了。当然View提供的set方法可能稍微更复杂一些,不过意思还是一样的。
下面继续看代码:
private ObjectAnimator(Object target, String propertyName) { mTarget = target; setPropertyName(propertyName); }
再看setFloatValues方法
public void setFloatValues(float... values) { //第一次初始化时,mValues肯定为null,这里初始化它 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 { //之前我们说过提供的值都被保存到PropertyValuesHolder //这里还提供了propertyName,标志改变的是哪个属性的值 //在初始化set,get方法实例时会被调用 setValues(PropertyValuesHolder.ofFloat(mPropertyName, values)); } } else { super.setFloatValues(values); } }
后续的setDuration,start动画,都和ValueAnimator一样,执行的流程都是相同的,不同的是ObjectAnimator重写了几个方法,让其执行了更多的操作,我们依次来看看重写了哪几个方法,
首先是重写了initAnimation(),此方法会在动画执行之前调用。
@Override void initAnimation() { if (!mInitialized) { //果然在PropertyValuesHolder中实例化了mTarget对象的对应 //属性的set,get方法,propertyName已经在实例化PropertyValuesHolder //的时候传递给了它。 int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].setupSetterAndGetter(mTarget); } super.initAnimation(); } }
现在具体看下setUpSetterAndGetter的实现细节
void setupSetterAndGetter(Object target) { Class targetClass = target.getClass(); if (mSetter == null) { setupSetter(targetClass); //初始化setter方法 } //之前在ValueAnimator中说过,一个keyFrame对应于一个传进来的 //value值,如果values值没有设置,会在这里调用get方法设置startValue for (Keyframe kf : mKeyframeSet.mKeyframes) { if (!kf.hasValue()) { if (mGetter == null) { //初始化getter方法 setupGetter(targetClass); } try { //通过get方法设置startValue kf.setValue(mGetter.invoke(target)); } catch (InvocationTargetException e) { Log.e("PropertyValuesHolder", e.toString()); } catch (IllegalAccessException e) { Log.e("PropertyValuesHolder", e.toString()); } } } }
下面是set,get方法具体实现
void setupSetter(Class targetClass) { mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType); } private void setupGetter(Class targetClass) { mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null); } /** * * @param targetClass 传入的target对应的class对象 * @param propertyMapMap 这是静态集合,缓存所有ObjectAnimator中target对应的set,get方法 * @param prefix set或get前缀 * @param valueType set方法对应的参数类型 * @return */ private Method setupSetterOrGetter(Class targetClass, HashMap<Class, HashMap<String, Method>> propertyMapMap, String prefix, Class valueType) { Method setterOrGetter = null; try { // Have to lock property map prior to reading it, to guard against // another thread putting something in there after we've checked it // but before we've added an entry to it mPropertyMapLock.writeLock().lock(); //先从缓存查看 HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass); if (propertyMap != null) { setterOrGetter = propertyMap.get(mPropertyName); } if (setterOrGetter == null) { //真正用反射获取set,get方法Method实例的地方 setterOrGetter = getPropertyFunction(targetClass, prefix, valueType); if (propertyMap == null) { propertyMap = new HashMap<String, Method>(); propertyMapMap.put(targetClass, propertyMap); } //缓存到集合 propertyMap.put(mPropertyName, setterOrGetter); } } finally { mPropertyMapLock.writeLock().unlock(); } return setterOrGetter; } private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { // TODO: faster implementation... Method returnVal = null; //根据前缀set或get,和传入的mPropertyName组合成方法名 //比如alpha会组合成setAlpha和getAlpha String methodName = getMethodName(prefix, mPropertyName); Class args[] = null; if (valueType == null) { //无参数,这是get方法 try { returnVal = targetClass.getMethod(methodName, args); } catch (NoSuchMethodException e) { Log.e("PropertyValuesHolder", "Couldn't find no-arg method for property " + mPropertyName + ": " + e); } } else { //获得set方法Method对象,这里稍微复杂一点,其实也很简单,就是重试了多次,选定多个参数 //type类型,防止一出错就放弃了, args = new Class[1]; Class typeVariants[]; if (mValueType.equals(Float.class)) { typeVariants = FLOAT_VARIANTS; } else if (mValueType.equals(Integer.class)) { typeVariants = INTEGER_VARIANTS; } else if (mValueType.equals(Double.class)) { typeVariants = DOUBLE_VARIANTS; } else { typeVariants = new Class[1]; typeVariants[0] = mValueType; } for (Class typeVariant : typeVariants) { args[0] = typeVariant; try { returnVal = targetClass.getMethod(methodName, args); // change the value type to suit mValueType = typeVariant; return returnVal; } catch (NoSuchMethodException e) { // Swallow the error and keep trying other variants } } } return returnVal; }
至此,set和get方法的初始化和缓存就全部结束了。
再看ValueAnimator重写的另一个方法animateValue
/** * 在ValueAnimator中知道,animateValue是在动画执行过程中, * 在animationFrame中执行的,这里除了执行父类的animateValue, * 计算每一帧得到的animationValue值,还把这个值通过setAnimatedValue * 设置到了目标对象的属性中 * @param fraction */ @Override void animateValue(float fraction) { super.animateValue(fraction); int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { //通过此方法,把父类计算出的animationValue //通过set方法设置目标对象传入的属性中 mValues[i].setAnimatedValue(mTarget); } }
看看其具体实现
void setAnimatedValue(Object target) { if (mSetter != null) { try { mTmpValueArray[0] = getAnimatedValue(); //反射执行set方法,传入每一帧计算得到的AnimatedValue值 mSetter.invoke(target, mTmpValueArray); } catch (InvocationTargetException e) { Log.e("PropertyValuesHolder", e.toString()); } catch (IllegalAccessException e) { Log.e("PropertyValuesHolder", e.toString()); } } }
到这里具体的实现就基本完成了,我们看到ObjectAnimator与ValueAnimator只有3个地方不同,
其一是提供了Object对象,以及对象的propertyName
其二是重写initAnimation方法,额外初始化了对应属性的set和get方法
其三就是重写animateValue,把每一帧计算的到的中间值通过set方法传入对象属性中。
ObjectAnimator实现基本就是这些东西。
3.更多
如果ValueAnimator要传入多个属性值有这几种方法
1.Multiple ObjectAnimator objects ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f); ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f); AnimatorSet animSetXY = new AnimatorSet(); animSetXY.playTogether(animX, animY); animSetXY.start(); 2.One ObjectAnimator PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f); ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start(); ViewPropertyAnimator 3. //这是view独有的 myView.animate().x(50f).y(100f);
如果要多次改变动画效果,可以直接设置keyFrame,
//前半段从0变到360度,后半段从360度变回0度 Keyframe kf0 = Keyframe.ofFloat(0f, 0f); Keyframe kf1 = Keyframe.ofFloat(.5f, 360f); Keyframe kf2 = Keyframe.ofFloat(1f, 0f); PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2); ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation) rotationAnim.setDuration(5000ms);
相关推荐
Android动画主要分为两种类型:属性动画(Property Animation)和视图动画(View Animation)。本篇将深入探讨这两种动画机制,以及如何在实际项目中应用它们。 1. **视图动画(View Animation)**:视图动画是...
**Android属性动画(Property Animation)完全解析** 属性动画是Android 3.0(API Level 11)引入的一种新动画机制,与视图动画(View Animation)不同,它不依赖于视图的绘制过程,而是直接改变对象的属性并实时...
通过分析和学习"Property Animation Demo"的源代码,开发者可以深入了解Property Animation的工作原理,并将其应用到自己的Android项目中,提升用户体验和应用的视觉吸引力。这个Demo对于初学者来说是一个很好的起点...
Android ICS 动画原理与实现分析主要涵盖了Android系统中动画机制的核心概念和技术。在Android ICS(Ice Cream Sandwich)版本中,动画已经成为提升用户体验的关键因素。本文将深入探讨动画的分类、框架原理以及实现...
Android提供了多种动画机制,包括视图动画(View Animation)、属性动画(Property Animation)以及过渡动画(Transition Animation)。以下是对这些动画类型的详细讲解: 1. 视图动画(View Animation): 视图...
此外,Android 3.0(API级别11)引入了属性动画(Property Animation)系统,这提供了更为强大的动画控制能力,包括对视图属性的实时改变和更复杂的动画效果。属性动画可以应用于任何对象,不仅限于View,因此开发者...
贝塞尔曲线属性动画是Android开发中的一个重要概念,它结合了属性动画系统和贝塞尔曲线的数学原理,使得UI元素能够沿着预定义的平滑路径移动,创造出丰富的视觉效果。在这个主题中,我们将深入探讨贝塞尔曲线的基本...
Android提供了三种主要类型的动画:属性动画(Property Animation)、视图动画(View Animation)和过渡动画(Transition Animation)。接下来,我们将深入探讨这三种动画机制。 1. 视图动画(View Animation): ...
随着Android系统的发展,出现了更强大的Animation框架,如Property Animation和Transition API,它们能更好地模拟真实世界的效果,并解决了View Animation的局限性。 总结,了解和掌握View Animation是Android...
2. **属性动画(Property Animation)**:自Android 3.0(API Level 11)引入,提供了更强大的动画功能,允许开发者对对象的属性进行操作,如平移、旋转、缩放等。属性动画包括`ObjectAnimator`、`ValueAnimator`和`...
Android中的动画主要分为两种类型:属性动画(Property Animation)和视图动画(View Animation)。视图动画在API级别11之前是主要的动画机制,它不改变View的实际状态,只是在屏幕上产生视觉效果。而属性动画自API...
通过分析这些源码,我们可以深入探讨Android中的动画系统,包括属性动画(Property Animation)、视图动画(View Animation)以及过渡动画(Transition Animation)。 1. **属性动画(Property Animation)** 属性...
Android提供了多种动画机制,包括帧动画(Frame Animation)和属性动画(Property Animation)。本文将深入探讨Android中的Animation使用,结合源码分析和实用工具,帮助开发者更好地理解和应用。 ### 1. 帧动画 ...
Android提供了两种主要的动画类型:属性动画(Property Animation)和视图动画(View Animation)。视图动画系统,如AlphaAnimation、ScaleAnimation、TranslateAnimation和RotateAnimation,主要用于旧版本的...
Android提供两种主要的动画机制:属性动画(Property Animation)和视图动画(View Animation)。属性动画自Android 3.0(API Level 11)引入,是一种强大的机制,允许开发者对对象的任何属性进行动画化,而不仅仅是...
1. **Android动画体系**:Android提供了两种主要的动画类型——属性动画(Property Animation)和视图动画(View Animation)。视图动画仅改变视觉效果,并不改变对象的实际状态,而属性动画可以真正改变对象的属性...
Android提供了多种动画机制,包括属性动画(Property Animation)、视图动画(View Animation)和框架动画(Frame Animation)。这篇博客文章“andorid animation”可能深入探讨了这些主题,并结合源码分析和实用...
在Android中,有两种主要的动画类型:属性动画(Property Animation)和视图动画(View Animation)。属性动画系统引入于API 11,可以对对象的属性进行平滑的变化,而视图动画则更早,主要用于改变视图的位置、大小...