Android动画原理
动画分类
补间动画
旋转、位移、透明度、缩放
属性动画
同样的属性动画也可以做到对View进行缩放、移动、旋转以及改变透明度;除此以外,它还能改变对象的某个属性。
ObjectAnimator.ofFloat()
ValueAnimator.start()
ObjectAnimator继承于ValueAnimator
public void start() {
start(false);
}
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
//省略部分代码...
//为动画添加回调
addAnimationCallback(0);
//判断动画开始时间是否需要延迟
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
startAnimation();
if (mSeekFraction == -1) {
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
addAnimationCallback(0);
这里调用了AnimationHandler类的addAnimationFrameCallback(),最终会调用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);
if (mCommitCallbacks.contains(callback)) {
getProvider().postCommitCallback(new Runnable() {
@Override
public void run() {
commitAnimationFrame(callback, getProvider().getFrameTime());
}
});
}
}
}
cleanUpList();
}
将一个callback丢到Choreographer中,当下一个Vsync信号来时执行doAnimationFrame()
/**
* Callbacks that receives notifications for animation timing and frame commit timing.
*/
interface AnimationFrameCallback {
boolean doAnimationFrame(long frameTime);
void commitAnimationFrame(long frameTime);
}
doAnimationFrame()中会执行到animateValue()
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);
}
}
}
mInterpolator就是插值器TimeInterpolator、TimeInterpolator接口提供了一个方法getInterpolation()获取当前属性随时间变化的百分比
public interface TimeInterpolator {
float getInterpolation(float input);
}
mValues[i].calculateValue(fraction);会调用估值器,将插值器计算出的值作为输入,计算出要变化的值设置给属性
public interface TypeEvaluator<T> {
public T evaluate(float fraction, T startValue, T endValue);
}
startAnimation()
startAnimation()会调用ininAnimation()方法而ObjectAnimation重写了该方法
void initAnimation() {
if (!mInitialized) {
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();
}
}
mValues为PropertyValuesHolder类型,从这可以看出调用了PropertyValuesHolder的setupSetterAndGetter方法,这个方法中查找了相对应属性的set/get方法。最终会调用 getPropertyFunction()方来获取相应的方法,若通过反射没有获取到相应的set/get方法时就会抛出 NoSuchMethodException异常
private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
// TODO: faster implementation...
Method returnVal = null;
//prefix表示set或get,然后在通过getMethodName()方法拼接出相应的方法名
String methodName = getMethodName(prefix, mPropertyName);
Class args[] = null;
//省略部分代码
returnVal = targetClass.getMethod(methodName, args);
return returnVal;
//省略部分代码
}
因为我们需要对View的translationX属性进行设置,所以通过这里可以看出我们设置属性时是通过View.setTranslationX()方法来设置的。
Choreographer
Choreographer是动画原理的一个核心,搞明白动画工作原理之前我们需要先了解Choreographer工作过程;
Choreographer工作过程简单描述如下:在Choreographer对象中有四条链表,分别保存着待处理的输入事件,待处理的动画事件,待处理的View绘制事件、待处理的post到Choreographer中事件,Android系统每隔16.7ms发出VSYNC信号,Choreographer.FrameDisplayEventReceiver收到信号后调用onVsync方法,最终会调用Choreographer.doFrame()方法,在doFrame方法中处理输入事件、动画事件、View绘制、post到Choreographer中的FrameCallback;我们可以使用Choreographer#postFrameCallback设置callback与Choreographer交互,设置的FrameCallCack(doFrame方法)会在下一个frame被渲染时触发(即下一个VSYNC到来时执行);Choreographer更多内容请参考Android Choreographer 源码分析
Android动画原理简单描述:将View的一次大的属性变化拆分为多次小的属性变化,在每次VSYNC信号到来时,根据当前时间和插值器来计算当前View属性的值,然后给View设置该属性值,直到动画执行完毕。其中Choreographer将动画拆分成一次次小的属性变化,Choreographer是动画的指挥者。理想情况下,属性刷新次数(动画拆分为多次小的属性变化) = 动画执行时间/16.7ms 。