通常用ValueAnimator实现动画的过渡效果,代码如下
ValueAnimator valueAnimator = ValueAnimator.ofFloat(1, 100);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
//获得当前动画的进度值,整型,1-100之间
int currentValue = (Integer)animator.getAnimatedValue();
}
});
valueAnimator.setDuration(450).start();
源码提供的创建ValueAnimator对象的方法有三个
ofArgb:接收颜色值作为参数,实现色值过渡的效果
ofFloat:接收float类型的参数
ofInt:接收int类型参数
这三个方法对应了不同的应用场景,原理是相同的,今天就以ofFloat方法为例做说明。可以看到这个方法只是创建了一个ValueAnimator对象,将values传递到构造方法中并返回了这个对象
ofFloat
/**
* 从下面的注释可以看出,ValueAnimator和ObjectAnimator有所不同,ValueAnimator典型
* 的方式就是接收两个参数,一个作为起始值,一个作为结束值,而ObjectAnimator是可以
* 传递一个参数作为结束值的,但是一个参数下ValueAnimator无法界定动画开始的值
* Constructs and returns a ValueAnimator that animates between float values. A single
* value implies that that value is the one being animated to. However, this is not typically
* useful in a ValueAnimator object because there is no way for the object to determine the
* starting value for the animation (unlike ObjectAnimator, which can derive that value
* from the target object and property being animated). Therefore, there should typically
* be two or more values.
*
* @param values A set of values that the animation will animate between over time.
* @return A ValueAnimator object that is set up to animate between the given values.
*/
public static ValueAnimator ofFloat(float... values) {
ValueAnimator anim = new ValueAnimator();
anim.setFloatValues(values);
return anim;
}
构造方法是空的,所以我们来看setFloatValues方法
/**
* <p>If there are already multiple sets of values defined for this ValueAnimator via more
* than one PropertyValuesHolder object, this method will set the values for the first
* of those objects.</p>
*
* @param values A set of values that the animation will animate between over time.
*/
public void setFloatValues(float... values) {
if (values == null || values.length == 0) {
return;
}
//mValues 是PropertyValuesHolder[]数组对象,这样判断的目的是看是否setFloatValues
//已经被调用过,如果有则已经存在这个值,此时就像注释所说,将values放在第一个位
//置,第一次肯定是null,所以先走setValues方法
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;
}
setValus方法分两步走,第一步是先将values包装成一个PropertyValuesHolder对象,PropertyValuesHolder是父类指向子类对象,ofFloat方法调用则返回的实际是FloatPropertyValuesHolder对象,它还有其他子类,比如IntPropertyValuesHolder,MultiFloatValuesHolder
PropertyValuesHolder.ofFloat("", values)
/**
* Constructs and returns a PropertyValuesHolder with a given property name and
* set of float values.
* @param propertyName The name of the property being animated.
* @param values The values that the named property will animate between.
* @return PropertyValuesHolder The constructed PropertyValuesHolder object.
*/
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
到了FloatPropertyValuesHolder构造方法中,可以看到两个参数的走向开始分开,一个传递给了父类的构造中,一个调用setFloatValues方法
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
setFloatValues(values);
}
propertyName在父类中被保存了起来,因为我们看的是ValueAnimator的源码,所以实际上这个参数没有用,它也是个空值
private PropertyValuesHolder(String propertyName) {
mPropertyName = propertyName;
}
重点来看setFloatValues方法
@Override
public void setFloatValues(float... values) {
super.setFloatValues(values);
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
先通过super来到父类的方法中
/**
* Set the animated values for this object to this set of floats.
* If there is only one value, it is assumed to be the end value of an animation,
* and an initial value will be derived, if possible, by calling a getter function
* on the object. Also, if any value is null, the value will be filled in when the animation
* starts in the same way. This mechanism of automatically getting null values only works
* if the PropertyValuesHolder object is used in conjunction
* {@link ObjectAnimator}, and with a getter function
* derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
* no way of determining what the value should be.
*
* @param values One or more values that the animation will animate between.
*/
public void setFloatValues(float... values) {
//float的Class类型
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
可以看出,这个方法就是为了将KeyFrame对象封装成FloatKeyframeSet返回,关键的代码在于KeyFrame中的ofFloat方法做了什么
public static KeyframeSet ofFloat(float... values) {
boolean badValue = false;
//计算可变参数的长度
int numKeyframes = values.length;
//创建出一个长度大于等于2的FloatKeyframe数组
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]);
//返回 a!=a,isNaN这个方法就是判断values[0]是不是不是数字,返回true,表示不是
//数字,那么badValue=true,表示是坏值
if (Float.isNaN(values[0])) {
badValue = true;
}
//如果可变参数length大于1
} else {
//这里假设numKeyframes是3,那么
//keyframes[0] = Keyframe.ofFloat(0f, values[0]);
//keyframes[1] = Keyframe.ofFloat(0.5f,value[1]);
//keyframes[2] = Keyframe.ofFloat(1f,values[2]);
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);
}
看看Keyframe.ofFloat,传入的第一个参数可以看作时间,这个时间是在总时间所占的比例,从0到1,第二个参数是在这个时间比例上所对应的值,这个值将是实际所展示出的值,如果是色值就是当前时间点位置对应的颜色值,如果是距离,那就是当前时间点位所在的位置
/**
* Constructs a Keyframe object with the given time and value. The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
* @param value The value that the object will animate to as the animation time approaches
* the time in this keyframe, and the the value animated from as the time passes the time in
* this keyframe.
*/
public static Keyframe ofFloat(float fraction, float value) {
//封装成FloatKeyframe对象,同样FloatKeyframe也是个父类指向子类对象,他也有其他
//子类,像IntKeyframe等
return new FloatKeyframe(fraction, value);
}
在这个构造方法中保存了传入的参数,将这些参数封装成了FloatKeyframe对象,那么也就是说,ValueAnimator的ofFloat方法传入几个可变参数,最终就会创建几个FloatKeyframe对象
FloatKeyframe(float fraction, float value) {
mFraction = fraction;
mValue = value;
mValueType = float.class;
mHasValue = true;
}
到了这里PropertyValuesHolder中的方法setFloatValues中的super.setFloatValues(values);执行完成,它的作用就是将可变参数values解析成了Keyframes,Keyframes是父类,真正的子类是FloatKeyframeSet,FloatKeyframeSet 继承 KeyframeSet ,KeyframeSet又继承Keyframes。成员变量mKeyframes中保存了FloatKeyframe对象的数组,每个对象封装了每个临界点的fraction比例和value值
然后将这个mKeyframes进行了转换,保存在FloatPropertyValuesHolder的成员变量Keyframes.FloatKeyframes mFloatKeyframes中
@Override
public void setFloatValues(float... values) {
super.setFloatValues(values);
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
到此为止PropertyValuesHolder.ofFloat("", values)方法执行完成,他的作用就是创建了FloatPropertyValuesHolder对象,并将可变参数values封装保存到了成员变量mFloatKeyframes中
然后就要开始执行setValues(PropertyValuesHolder.ofFloat("", values))方法了,这里是将PropertyValuesHolder保存在了一个map中
/**
* Sets the values, per property, being animated between. This function is called internally
* by the constructors of ValueAnimator that take a list of values. But a ValueAnimator can
* be constructed without values and this method can be called to set the values manually
* instead.
*
* @param values The set of values, per property, being animated between.
*/
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];
//以属性名为key,但其实因为是valueAnimator,所以key是空字符串
mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
这时第一次调用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 {
//如果mValues已经存在了,说明有过调用,此时就取出PropertyValuesHolder[] mValues
//数组中的第一个PropertyValuesHolder 对象,通过调用它的setFloatValues方法
//更新其成员变量mKeyframes的值
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setFloatValues(values);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
setValues方法结束了,也可以说是ValueAnimator的ofFloat方法也结束了,对于ValueAnimator来说,调用ofFloat的逻辑简单总结一下做了哪些事情
第一,将values可变参数数组封装成FloatPropertyValuesHolder对象,对象中的变量Keyframes.FloatKeyframes mFloatKeyframes保存了fraction和value的数据
第二,将这个FloatPropertyValuesHolder放入HashMap集合,以propertyName为key值存放
setDuration
保存动画执行的时间mDuration
/**
* Sets the length of the animation. The default duration is 300 milliseconds.
*
* @param duration The length of the animation, in milliseconds. This value cannot
* be negative.
* @return ValueAnimator The object called with setDuration(). This return
* value makes it easier to compose statements together that construct and then set the
* duration, as in <code>ValueAnimator.ofInt(0, 10).setDuration(500).start()</code>.
*/
@Override
public ValueAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
mDuration = duration;
return this;
}
addUpdateListener
将监听器存入集合,也就是可以添加大于一个的listener
/**
* Adds a listener to the set of listeners that are sent update events through the life of
* an animation. This method is called on all listeners for every frame of the animation,
* after the values for the animation have been calculated.
*
* @param listener the listener to be added to the current set of listeners for this animation.
*/
public void addUpdateListener(AnimatorUpdateListener listener) {
if (mUpdateListeners == null) {
mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
}
mUpdateListeners.add(listener);
}
start
start方法是最关键的方法
@Override
public void start() {
start(false);
}
/**
* Start the animation playing. This version of start() takes a boolean flag that indicates
* whether the animation should play in reverse. The flag is usually false, but may be set
* to true if called from the reverse() method.
*
* <p>The animation started by calling this method will be run on the thread that called
* this method. This thread should have a Looper on it (a runtime exception will be thrown if
* this is not the case). Also, if the animation will animate
* properties of objects in the view hierarchy, then the calling thread should be the UI
* thread for that view hierarchy.</p>
*
* @param playBackwards Whether the ValueAnimator should start playing in reverse.
*/
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
//playBackwards表示是否倒序执行,一般否是false,除非你有特殊需求这样设置
//mSuppressSelfPulseRequested,这个值通过方法startWithoutPulsing设置,就把他默认当作false即可
mReversing = playBackwards;
mSelfPulse = !mSuppressSelfPulseRequested;
// 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 = -1;
mFirstFrameTime = -1;
mStartTime = -1;
addAnimationCallback(0);
//mStartDelay 是延迟播放动画的时间,通过调用setStartDelay设置,表示延迟多久可以
//执行动画,默认不延迟为0
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
// 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);
}
}
}
startAnimation
/**
* Called internally to start an animation by adding it to the active animations list. Must be
* called on the UI thread.
*/
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;
}
//这个mListeners 指的是addListener添加的回调,包括onAnimationStart
//onAnimationEnd onAnimationCancel onAnimationRepeat
if (mListeners != null) {
notifyStartListeners();
}
}
initAnimation
/**
* This function is called immediately before processing the first animation
* frame of an animation. If there is a nonzero <code>startDelay</code>, the
* function is called after that delay ends.
* It takes care of the final initialization steps for the
* animation.
*
* <p>Overrides of this method should call the superclass method to ensure
* that internal mechanisms for the animation are set up correctly.</p>
*/
//如果你的API允许使用者重写你的方法,但是呢,你又需要你自己的方法(父方法)在重写的
//时候也被调用,这时候你可以使用@CallSuper标注,当重写的方法没有调用父方法时,工
//具就会给予标记提示
@CallSuper
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
//每个PropertyValuesHolder的init方法都被调用
mValues[i].init();
}
mInitialized = true;
}
}
来到PropertyValuesHolder中的init方法
/**
* Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
* to calculate animated values.
*/
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
//我们已经知道Keyframes是一个接口,所以这里又要去它的实现类中看setEvaluator
//方法-----FloatKeyframeSet
mKeyframes.setEvaluator(mEvaluator);
}
}
FloatKeyframeSet --> KeyframeSet --> Keyframes 三者的继承实现关系
来到FloatKeyframeSet中发现没有setEvaluator方法,那么再来到KeyframeSet 中,可以看到这里仅仅是保存了Evaluator,并没有做别的,那么动画的逻辑在哪,再回过头看一看
/**
* Sets the TypeEvaluator to be used when calculating animated values. This object
* is required only for KeyframeSets that are not either IntKeyframeSet or FloatKeyframeSet,
* both of which assume their own evaluator to speed up calculations with those primitive
* types.
*
* @param evaluator The TypeEvaluator to be used to calculate animated values.
*/
public void setEvaluator(TypeEvaluator evaluator) {
mEvaluator = evaluator;
}
回来再看看这一段代码,你会发现注释的第一句话这样说到,这个功能是再动画开始前执行的,也就是这只是个初始化的动作,给每一个PropertyValuesHolder中的mKeyframes设置Evaluator
/**
* This function is called immediately before processing the first animation
* frame of an animation. If there is a nonzero <code>startDelay</code>, the
* function is called after that delay ends.
* It takes care of the final initialization steps for the
* animation.
*
* <p>Overrides of this method should call the superclass method to ensure
* that internal mechanisms for the animation are set up correctly.</p>
*/
@CallSuper
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
再往上一层看startAnimation方法,从注释中也能看出一点这个方法只是初始化并通过观察者
// 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();
再回到这里,你会发现原来动画逻辑的开始是在则个位置,之前是被startAnimation这个方法名骗了
private void start(boolean playBackwards) {
......
startAnimation();
//默认情况下mSeekFraction =-1
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);
}
......
}
fraction是动画执行的百分比,如果动画执行时长是0,那么就设置一步到位,否则就设置fraction为当前比例
/**
* Sets the position of the animation to the specified point in time. This time should
* be between 0 and the total duration of the animation, including any repetition. If
* the animation has not yet been started, then it will not advance forward after it is
* set to this time; it will simply set the time to this value and perform any appropriate
* actions based on that time. If the animation is already running, then setCurrentPlayTime()
* will set the current playing time to this value and continue playing from that point.
*
* @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
*/
public void setCurrentPlayTime(long playTime) {
float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
setCurrentFraction(fraction);
}
/**
* Sets the position of the animation to the specified fraction. This fraction should
* be between 0 and the total fraction of the animation, including any repetition. That is,
* a fraction of 0 will position the animation at the beginning, a value of 1 at the end,
* and a value of 2 at the end of a reversing animator that repeats once. If
* the animation has not yet been started, then it will not advance forward after it is
* set to this fraction; it will simply set the fraction to this value and perform any
* appropriate actions based on that fraction. If the animation is already running, then
* setCurrentFraction() will set the current fraction to this value and continue
* playing from that point. {@link Animator.AnimatorListener} events are not called
* due to changing the fraction; those events are only processed while the animation
* is running.
*
* @param fraction The fraction to which the animation is advanced or rewound. Values
* outside the range of 0 to the maximum fraction for the animator will be clamped to
* the correct range.
*/
public void setCurrentFraction(float fraction) {
initAnimation();
//把fraction限制在一个正确的范围内,这个范围就是0-repeatCount+1
fraction = clampFraction(fraction);
mStartTimeCommitted = true; // do not allow start time to be compensated for jank
//表示判断是否已经进入了动画循环中,这个判断不同于isRunning(),isRunning返回true
//也可能没有进入动画循环,所以,这个判断更加准确,进入循环之后会给
//mLastFrameTime赋值,而mRunning = true是在startAnimation初始化动画的时候就设
//置了,所以mRunning值的设置是早于mLastFrameTime的
if (isPulsingInternal()) {
long seekTime = (long) (getScaledDuration() * fraction);
long currentTime = AnimationUtils.currentAnimationTimeMillis();
// Only modify the start time when the animation is running. Seek fraction will ensure
// non-running animations skip to the correct start time.
mStartTime = currentTime - seekTime;
} else {
// If the animation loop hasn't started, or during start delay, the startTime will be
// adjusted once the delay has passed based on seek fraction.
mSeekFraction = fraction;
}
mOverallFraction = fraction;
//获取本次循环当前动画执行的fraction,因为可能这个动画被设置要重复执行多次
final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);
animateValue(currentIterationFraction);
}
/**
* Clamps fraction into the correct range: [0, mRepeatCount + 1]. If repeat count is infinite,
* no upper bound will be set for the fraction.
*
* @param fraction fraction to be clamped
* @return fraction clamped into the range of [0, mRepeatCount + 1]
*/
private float clampFraction(float fraction) {
if (fraction < 0) {
fraction = 0;
} else if (mRepeatCount != INFINITE) {
fraction = Math.min(fraction, mRepeatCount + 1);
}
return fraction;
}
/**
* Calculates the fraction of the current iteration, taking into account whether the animation
* should be played backwards. E.g. When the animation is played backwards in an iteration,
* the fraction for that iteration will go from 1f to 0f.
*/
private float getCurrentIterationFraction(float fraction, boolean inReverse) {
//在此验证fraction是否在合理范围,不在则限制其在合理范围
fraction = clampFraction(fraction);
//获取当前动画执行的第几个循环
int iteration = getCurrentIteration(fraction);
//当前fraction,它可能是2.3,那么iteration则是2,2.3减去2得到的就是当前
//本次循环的fraction
float currentFraction = fraction - iteration;
//根据是否反向执行返回正确的fraction
return shouldPlayBackward(iteration, inReverse) ? 1f - currentFraction : currentFraction;
}
mValues数组中存放的是FloatPropertyValuesHoler,我们可以在其父类PropertyValuesHolder中找到calculateValue方法
/**
* This method is called with the elapsed fraction of the animation during every
* animation frame. This function turns the elapsed fraction into an interpolated fraction
* and then into an animated value (from the evaluator. The function is called mostly during
* animation updates, but it is also called when the <code>end()</code>
* function is called, to set the final value on the property.
*
* <p>Overrides of this method must call the superclass to perform the calculation
* of the animated value.</p>
*
* @param fraction The elapsed fraction of the animation.
*/
@CallSuper
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
//计算出fraction对应的值
for (int i = 0; i < numValues; ++i) {
mValues[i].calculateValue(fraction);
}
//这是我们设置的UpdateListener回调
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
在PropertyValuesHolder中找到了这个方法,作用就是根据设置的差值器获取对应fraction的value
/**
* Function used to calculate the value according to the evaluator set up for
* this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
*
* @param fraction The elapsed, interpolated fraction of the animation.
*/
void calculateValue(float fraction) {
Object value = mKeyframes.getValue(fraction);
mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}
但是我们会发现在他的子类中也都重写了这个方法,比如FloatPropertyValuesHolder是这样写的,直接从对象中取出对应的值,这个mFloatKeyframes是在ofFloat阶段创建出来的,具体怎么取值的,请自行阅读
@Override
void calculateValue(float fraction) {
mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
}
然后我们回到上边继续看UpdateListener的逻辑
mUpdateListeners.get(i).onAnimationUpdate(this);
当这行代码执行的时候,我们设置的监听器就会收到回调,也就是终于回到了,这里了
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
//获得当前动画的进度值,整型,1-100之间
int currentValue = (Integer)animator.getAnimatedValue();
}
});
此时因为之前的一系列操作已经将每个fraction的值保存起来了,那么就可以如此获取了
public Object getAnimatedValue() {
if (mValues != null && mValues.length > 0) {
return mValues[0].getAnimatedValue();
}
// Shouldn't get here; should always have values unless ValueAnimator was set up wrong
return null;
}
总结一下
ValueAnimator虽然是属性动画,但它并不帮我们做动画,它只是将每个阶段fraction对应的值通过差值器给我们计算出来并保存,然后动画的执行由我们去开启。