属性动画PropertyAnimation
实例
Button icon = (Button) findViewById(R.id.button1);
ValueAnimator valueAnimator = ValueAnimator.ofInt(200, 400, 200);
valueAnimator.setDuration(2000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int currentPoint = (int) animation.getAnimatedValue();
Log.e("zhen", "currentValue: " + currentPoint);
icon.setX(currentPoint);
icon.setY(currentPoint);
}
});
valueAnimator.start();
关键帧的处理
以int类型为例
-
PropertyValuesHolder.ofInt("", values)返回一个IntPropertyValuesHolder对象
-
静态内部类IntPropertyValuesHolder 继承自PropertyValueHolder,内部持有KeyframeSet关键帧集合
-
KeyframeSet.ofInt(values) 计算得到int类型的关键帧集合,IntKeyframeSet
-
关键帧Keyframe主要持有两个属性,一个fraction,一个value (fraction等比分)
ValueAnimator
持有一个PropertyValuesHolder的数组引用
//ValueAnimator.java
PropertyValuesHolder[] mValues;
//构建并返回一个ValueAnimator,
public static ValueAnimator ofInt(int... values) {
ValueAnimator anim = new ValueAnimator();
anim.setIntValues(values);
return anim;
}
public void setIntValues(int... values) {
if (mValues == null || mValues.length == 0) { //第一次走这里
setValues(PropertyValuesHolder.ofInt("", values)); //返回IntPropertyValuesHolder
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setIntValues(values);
}
}
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
mValues = values;
}
PropertyValueHolder
- 传入values值,计算得到mKeyframes
//PropertyValueHolder.java
Class mValueType;
Keyframes mKeyframes = null;
//指定value的类型和mKeyframes,mKeyframes 是 IntKeyframeSet类型
public void setIntValues(int... values) {
mValueType = int.class;
mKeyframes = KeyframeSet.ofInt(values); //计算得到mKeyframes
}
//返回一个IntPropertyValuesHolder对象
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
return new IntPropertyValuesHolder(propertyName, values);
}
static class IntPropertyValuesHolder extends PropertyValuesHolder {
Keyframes.IntKeyframes mIntKeyframes;
int mIntAnimatedValue;
public IntPropertyValuesHolder(String propertyName, int... values) {
super(propertyName);
setIntValues(values);
}
public void setIntValues(int... values) {
super.setIntValues(values); //计算mKeyframes
mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
}
@Override
//在vsync信号到达时,会回调这个,然后根据动画进度比例返回动画当前值
void calculateValue(float fraction) {
//回调IntKeyframeSet.getIntValue(fraction)
mIntAnimatedValue = mIntKeyframes.getIntValue(fraction);
}
}
KeyframeSet 关键帧集合
public class KeyframeSet implements Keyframes {
int mNumKeyframes;
TimeInterpolator mInterpolator; // only used in the 2-keyframe case
List<Keyframe> mKeyframes; // only used when there are not 2 keyframes
TypeEvaluator mEvaluator;
//IntKeyframe
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
mKeyframes = Arrays.asList(keyframes);
mFirstKeyframe = keyframes[0];
mLastKeyframe = keyframes[mNumKeyframes - 1];
mInterpolator = mLastKeyframe.getInterpolator();
}
//返回IntKeyframeSet对象
public static KeyframeSet ofInt(int... values) {
int numKeyframes = values.length;
IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
//如果只有一个value,value过渡是0-value
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
} else {
//如果value个数大于等于2
//比如说 100-200,fraction 对应 0-> 1/1
//比如说 100-200-300-400,fraction对应 0-> 1/3-> 2/3-> 3/3
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);
}
}
Keyframe关键帧
public abstract class Keyframe implements Cloneable {
boolean mHasValue;
float mFraction; //动画进度
Class mValueType; //value类型
private TimeInterpolator mInterpolator = null; //插值器
//构建一个Int类型的关键帧
public static Keyframe ofInt(float fraction, int value) {
return new IntKeyframe(fraction, value);
}
static class IntKeyframe extends Keyframe {
int mValue;
IntKeyframe(float fraction, int value) {
mFraction = fraction;
mValue = value;
mValueType = int.class;
}
}
}
AnimationHandler
当 ValueAnimator 调用了 start() 方法之后,首先会对一些变量进行初始化工作并通知动画开始了,然后 ValueAnimator 实现了 AnimationFrameCallback 接口,并通过 AnimationHander 将自身 this 作为参数传到 mAnimationCallbacks 列表里缓存起来。而 AnimationHandler 在 mAnimationCallbacks 列表大小为 0 时会通过内部类 MyFrameCallbackProvider 将一个 mFrameCallback 工作缓存到 Choreographer 的待执行队列里,并向底层注册监听下一个屏幕刷新信号事件。
当屏幕刷新信号到的时候,Choreographer 的 doFrame() 会去将这些待执行队列里的工作取出来执行,那么此时也就回调了 AnimationHandler 的 mFrameCallback 工作。
//所有属性动画的管理处理者
public class AnimationHandler {
//缓存延时动画
private final ArrayMap<AnimationFrameCallback, Long> mDelayedCallbackStartTime = new ArrayMap<>();
//缓存AnimationFrameCallback,每个帧回调时,取出来执行
private final ArrayList<AnimationFrameCallback> mAnimationCallbacks = new ArrayList<>();
//用于补偿校对第一帧动画的延时
private final ArrayList<AnimationFrameCallback> mCommitCallbacks = new ArrayList<>();
//自定义MyFrameCallbackProvider,拓展性
private AnimationFrameCallbackProvider mProvider;
//收到底层vsync信号,会回调doFrame方法
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime()); //处理当前时间的所有动画
if (mAnimationCallbacks.size() > 0) { //如果有未执行完成的动画,继续注册,监听下一个frame
getProvider().postFrameCallback(this);
}
}
};
public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
private boolean mListDirty = false; //记录是否有待清除的AnimationFrameCallback
//得到AnimationHandler的实例(每一个线程都有一个?)
public static AnimationHandler getInstance() {
if (sAnimatorHandler.get() == null) {
sAnimatorHandler.set(new AnimationHandler());
}
return sAnimatorHandler.get();
}
//AnimationFrameCallback 缓存,并注册监听下一个frame回调事件
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) { //注册底层frame监听事件,
getProvider().postFrameCallback(mFrameCallback);
}
//mAnimationCallbacks用于记录每一个Animation
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
//如果Animation有延时,则放入mDelayedCallbackStartTime
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
}
public void addOneShotCommitCallback(final AnimationFrameCallback callback) {
if (!mCommitCallbacks.contains(callback)) {
mCommitCallbacks.add(callback);
}
}
//移除AnimationFrameCallback
public void removeCallback(AnimationFrameCallback callback) {
mCommitCallbacks.remove(callback);
mDelayedCallbackStartTime.remove(callback);
int id = mAnimationCallbacks.indexOf(callback);
if (id >= 0) { //只是将其设为null,待doAnimationFrame再清除?
mAnimationCallbacks.set(id, null);
mListDirty = true;
}
}
//遍历缓存的所有AnimationFrameCallback,执行回调callback.doAnimationFrame
private void doAnimationFrame(long frameTime) {
long currentTime = SystemClock.uptimeMillis();
final int size = mAnimationCallbacks.size();
for (int i = 0; i < size; i++) {
final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
if (callback == null) {
continue;
}
if (isCallbackDue(callback, currentTime)) {
callback.doAnimationFrame(frameTime); //回调Animation的doAnimationFrame
if (mCommitCallbacks.contains(callback)) { //如果需要补偿校正动画第一帧
getProvider().postCommitCallback(new Runnable() {
@Override
public void run() {
commitAnimationFrame(callback, getProvider().getFrameTime());
}
});
}
}
}
cleanUpList(); //清除执行完成的Animation
}
private void commitAnimationFrame(AnimationFrameCallback callback, long frameTime) {
if (!mDelayedCallbackStartTime.containsKey(callback) &&
mCommitCallbacks.contains(callback)) {
callback.commitAnimationFrame(frameTime); //回调Animator中的方法
mCommitCallbacks.remove(callback);
}
}
//清理mAnimationCallbacks中已经执行完的动画,全部执行完,则不会继续监听frame事件
private void cleanUpList() {
if (mListDirty) {
for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
if (mAnimationCallbacks.get(i) == null) {
mAnimationCallbacks.remove(i);
}
}
mListDirty = false;
}
}
//判断是否应该执行这个callback
private boolean isCallbackDue(AnimationFrameCallback callback, long currentTime) {
Long startTime = mDelayedCallbackStartTime.get(callback);
if (startTime == null) {
return true; //没有设置延时
}
if (startTime < currentTime) { //延时时间已经过了
mDelayedCallbackStartTime.remove(callback);
return true;
}
return false;
}
//Animation实现了AnimationFrameCallback ,即doFrame会回调Animation的doAnimationFrame
interface AnimationFrameCallback {
boolean doAnimationFrame(long frameTime); //@return if the animation has finished
void commitAnimationFrame(long frameTime);
}
//得到一个AnimationFrameCallbackProvider
private AnimationFrameCallbackProvider getProvider() {
if (mProvider == null) {
mProvider = new MyFrameCallbackProvider();
}
return mProvider;
}
public interface AnimationFrameCallbackProvider {
void postFrameCallback(Choreographer.FrameCallback callback);
void postCommitCallback(Runnable runnable);
long getFrameTime();
long getFrameDelay();
void setFrameDelay(long delay);
}
//MyFrameCallbackProvider 实现了 AnimationFrameCallbackProvider
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
final Choreographer mChoreographer = Choreographer.getInstance();
@Override
public void postFrameCallback(Choreographer.FrameCallback callback) {
//注册一个frame的监听事件,则下一个16.6ms到来时,会回调callback的doFrame()
mChoreographer.postFrameCallback(callback);
}
}
}
动画真正执行点
//ValueAnimator.java
private TimeInterpolator mInterpolator = sDefaultInterpolator;
private static final TimeInterpolator sDefaultInterpolator =
new AccelerateDecelerateInterpolator();
public final boolean doAnimationFrame(long frameTime) {
final long currentTime = Math.max(frameTime, mStartTime);
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
endAnimation();
}
return finished;
}
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
final long scaledDuration = getScaledDuration(); //默认为动画时长
//动画进度
final float fraction = scaledDuration > 0 ?
(float)(currentTime - mStartTime) / scaledDuration : 1f;
//判断动画是否结束done,并适当回调onAnimationRepeat
mOverallFraction = clampFraction(fraction);
//currentIterationFraction 限制在0-1之间
float currentIterationFraction = getCurrentIterationFraction(
mOverallFraction, mReversing);
//currentIterationFraction
animateValue(currentIterationFraction);
}
return done;
}
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
//mValues是IntPropertyValuesHolder对象
//回调IntPropertyValuesHolder.calculateValue(fraction)
mValues[i].calculateValue(fraction);
}
//回调onAnimationUpdate
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
//动画执行完毕
private void endAnimation() {
removeAnimationCallback();
//回调onAnimationEnd
}
//回调AnimationHandler,将对应的AnimationFrameCallback置为null,在clear的时候清除掉
private void removeAnimationCallback() {
getAnimationHandler().removeCallback(this);
}
//IntPropertyValuesHolder.class
void calculateValue(float fraction) {
mIntAnimatedValue = mIntKeyframes.getIntValue(fraction);
}
IntKeyframeSet 继承自KeyframeSet ,实现了Keyframes.IntKeyframes接口
//IntKeyframeSet.java
class IntKeyframeSet extends KeyframeSet implements Keyframes.IntKeyframes {
public IntKeyframeSet(IntKeyframe... keyframes) {
super(keyframes);
}
@Override
public Object getValue(float fraction) {
return getIntValue(fraction);
}
@Override
public int getIntValue(float fraction) {
if (fraction <= 0f) {
//第一帧的处理
} else if (fraction >= 1f) {
//最后一帧的处理
}
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();
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
int prevValue = prevKeyframe.getIntValue();
int nextValue = nextKeyframe.getIntValue();
// Apply interpolator on the proportional duration.
if (interpolator != null) {
intervalFraction = interpolator.getInterpolation(intervalFraction);
}
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();
}
@Override
public Class getType() {
return Integer.class;
}
}
总结:
-
ValueAnimator 属性动画调用了 start() 之后,会先去进行一些初始化工作,包括变量的初始化、通知动画开始事件;
-
然后通过 AnimationHandler 将其自身 this 添加到 mAnimationCallbacks 队列里,AnimationHandller 是一个单例类,为所有的属性动画服务,列表里存放着所有正在进行或准备开始的属性动画;
-
如果当前存在要运行的动画,那么 AnimationHandler 会去通过 Choreographer 向底层注册监听下一个屏幕刷新信号,当接收到信号时,它的 mFrameCallback 会开始进行工作,工作的内容包括遍历列表来分别处理每个属性动画在当前帧的行为,处理完列表中的所有动画后,如果列表还不为 0,那么它又会通过 Choreographer 再去向底层注册监听下一个屏幕刷新信号事件,如此反复,直至所有的动画都结束。
-
AnimationHandler 遍历列表处理动画是在 doAnimationFrame() 中进行,而具体每个动画的处理逻辑则是在各自,也就是 ValueAnimator 的 doAnimationFrame() 中进行,各个动画如果处理完自身的工作后发现动画已经结束了,那么会将其在列表中的引用赋值为空,AnimationHandler 最后会去将列表中所有为 null 的都移除掉,来清理资源。
-
每个动画 ValueAnimator 在处理自身的动画行为时,首先,如果当前是动画的第一帧,那么会根据是否有"跳过片头"(setCurrentPlayTime())来记录当前动画第一帧的时间 mStartTime 应该是什么。
-
第一帧的动画其实也就是记录 mStartTime 的时间以及一些变量的初始化而已,动画进度仍然是 0,所以下一帧才是动画开始的关键,但由于属性动画的处理工作是在绘制界面之前的,那么有可能因为绘制耗时,而导致 mStartTime 记录的第一帧时间与第二帧之间隔得太久,造成丢了开头的多帧,所以如果是这种情况下,会进行 mStartTime 的修正。
-
修正的具体做法则是当绘制工作完成后,此时,再根据当前时间与 mStartTime 记录的时间做比较,然后进行修正。
-
每个动画在处理当前帧的动画逻辑时,首先会先根据当前时间和动画第一帧时间以及动画的持续时长来初步计算出当前帧时动画所处的进度,然后会将这个进度值等价转换到 0-1 区间之内。
-
接着,插值器会将这个经过初步计算之后的进度值根据设定的规则计算出实际的动画进度值,取值也是在 0-1 区间内。
-
计算出当前帧动画的实际进度之后,会将这个进度值交给关键帧机制,来换算出我们需要的值,比如 ValueAnimator.ofInt(0, 100) 表示我们需要的值变化范围是从 0-100,那么插值器计算出的进度值是 0-1 之间的,接下去就需要借助关键帧机制来映射到 0-100 之间。
-
关键帧的数量是由 ValueAnimator.ofInt(0, 1, 2, 3) 参数的数量来决定的,比如这个就有四个关键帧,第一帧和最后一帧是必须的,所以最少会有两个关键帧,如果参数只有一个,那么第一帧默认为 0,最后一帧就是参数的值。当调用了这个 ofInt() 方法时,关键帧组也就被创建了。
-
当只有两个关键帧时,映射的规则是,如果没有设置估值器,那么就等比例映射,比如动画进度为 0.5,需要的值变化区间是 0-100,那么等比例映射后的值就是 50,那么我们在 onAnimationUpdate 的回调中通过 animation.getAnimatedValue() 获取到的值 50 就是这么来的。
-
如果有设置估值器,那么就按估值器的规则来进行映射。
-
当关键帧超过两个时,需要先找到当前动画进度是落于哪两个关键帧之间,然后将这个进度值先映射到这两个关键帧之间的取值,接着就可以将这两个关键帧看成是第一帧和最后一帧,那么就可以按照只有两个关键帧的情况下的映射规则来进行计算了。
-
而进度值映射到两个关键帧之间的取值,这就需要知道每个关键帧在整个关键帧组中的位置信息,或者说权重。而这个位置信息是在创建每个关键帧时就传进来的。onInt() 的规则是所有关键帧按等比例来分配权重,比如有三个关键帧,第一帧是 0,那么第二帧就是 0.5, 最后一帧 1。