我们知道视图动画的启动方式如下:
ImageView image = (ImageView) findViewById(R.id.image); Animation hyperspaceJump = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump); image.startAnimation(hyperspaceJump);
private boolean drawAnimation(ViewGroup parent, long drawingTime, Animation a, boolean scalingRequired) { Transformation invalidationTransform; final int flags = parent.mGroupFlags; final boolean initialized = a.isInitialized(); if (!initialized) { a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight()); a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);//初始化了一个矩形区域,mRight,mLeft,mBottom,mTop是根据该View得到的区域参数 if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler); onAnimationStart(); } boolean more = a.getTransformation(drawingTime, parent.mChildTransformation, 1f);//第一个重要函数,后面源码分析,这里我们关注parent.mChildTransformation这个参数 if (scalingRequired && mAttachInfo.mApplicationScale != 1f) { if (parent.mInvalidationTransformation == null) { parent.mInvalidationTransformation = new Transformation(); } invalidationTransform = parent.mInvalidationTransformation; a.getTransformation(drawingTime, invalidationTransform, 1f);//比较这个if-else,不管走if还是else,我们可以看到它都走了a.getTransformation这个函数。 } else { invalidationTransform = parent.mChildTransformation;//不管是if,还是else,invalidationTransform的值都来自于对getTransformation的调用 } if (more) { if (!a.willChangeBounds()) {//除了alphaAnimation,其它的该函数都return true if ((flags & (ViewGroup.FLAG_OPTIMIZE_INVALIDATE | ViewGroup.FLAG_ANIMATION_DONE)) == ViewGroup.FLAG_OPTIMIZE_INVALIDATE) { parent.mGroupFlags |= ViewGroup.FLAG_INVALIDATE_REQUIRED; } else if ((flags & ViewGroup.FLAG_INVALIDATE_REQUIRED) == 0) { // The child need to draw an animation, potentially offscreen, so // make sure we do not cancel invalidate requests parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION; parent.invalidate(mLeft, mTop, mRight, mBottom);//对于AlphaAnimation,视图大小没有任何变化 } } else { if (parent.mInvalidateRegion == null) { parent.mInvalidateRegion = new RectF(); } final RectF region = parent.mInvalidateRegion; a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region, invalidationTransform);//第二个重要函数,举行参数region动态的得到了下一桢的区域大小 // The child need to draw an animation, potentially offscreen, so // make sure we do not cancel invalidate requests parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION; final int left = mLeft + (int) region.left;//这两个final值不断的更新得到新的区域值 final int top = mTop + (int) region.top; parent.invalidate(left, top, left + (int) (region.width() + .5f),//绘制,并刷新下一桢,得到下一个区域大小。 top + (int) (region.height() + .5f)); } } return more; }
通过上面这个函数可以了解到,视图通过不断的刷新并得到Animation提供的视图区域的大小的参数而绘制出动态视图。下面来看看上面提到的两个重要函数,通过这两个函数,可以看出Animation是如何提供动态的区域参数的。
/** * Gets the transformation to apply at a specified point in time. Implementations of this * method should always replace the specified Transformation or document they are doing * otherwise. * * @param currentTime Where we are in the animation. This is wall clock time. * @param outTransformation A transformation object that is provided by the * caller and will be filled in by the animation. * @return True if the animation is still running */ //上面的这段注释清晰的说明了这个函数的功能————在某个指定的时间点获取一个Transformation对象 public boolean getTransformation(long currentTime, Transformation outTransformation) { if (mStartTime == -1) { mStartTime = currentTime; } final long startOffset = getStartOffset(); final long duration = mDuration; float normalizedTime; if (duration != 0) { normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) / (float) duration; } else { // time is a step-change with a zero duration normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f; } final boolean expired = normalizedTime >= 1.0f; mMore = !expired; if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) { if (!mStarted) { fireAnimationStart(); mStarted = true; if (USE_CLOSEGUARD) { guard.open("cancel or detach or getTransformation"); } } if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); if (mCycleFlip) { normalizedTime = 1.0f - normalizedTime; } final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);//插值器,提供不同的时间点 applyTransformation(interpolatedTime, outTransformation); } ... }所有继承自Animation类的函数都需要重写applyTransformation()函数,正是在这个函数中,指定的不同的动画效果所需要的变化。我们以TranslateAnimation的该函数作为示例:
protected void applyTransformation(float interpolatedTime, Transformation t) { float dx = mFromXDelta; float dy = mFromYDelta; if (mFromXDelta != mToXDelta) { dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime); } if (mFromYDelta != mToYDelta) { dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime); } t.getMatrix().setTranslate(dx, dy); }
public void getInvalidateRegion(int left, int top, int right, int bottom, RectF invalidate, Transformation transformation) { final RectF tempRegion = mRegion; final RectF previousRegion = mPreviousRegion; invalidate.set(left, top, right, bottom); transformation.getMatrix().mapRect(invalidate); // Enlarge the invalidate region to account for rounding errors invalidate.inset(-1.0f, -1.0f); tempRegion.set(invalidate); invalidate.union(previousRegion); previousRegion.set(tempRegion); final Transformation tempTransformation = mTransformation; final Transformation previousTransformation = mPreviousTransformation; tempTransformation.set(transformation); transformation.set(previousTransformation); previousTransformation.set(tempTransformation); }
根据上面这些代码,可以窥见到Animation.java是如何影响到view不停刷新绘制,从而得到动态效果的。下面解释前面的一些疑问:
先来看看插值器的工作原理
public class AccelerateInterpolator implements Interpolator {
private final float mFactor;
private final double mDoubleFactor;
public AccelerateInterpolator(Context context, AttributeSet attrs) {
TypedArray a =
context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AccelerateInterpolator);
mFactor = a.getFloat(com.android.internal.R.styleable.AccelerateInterpolator_factor, 1.0f);
mDoubleFactor = 2 * mFactor;
a.recycle();
}
public float getInterpolation(float input) {
if (mFactor == 1.0f) {
return input * input;
} else {
return (float)Math.pow(input, mDoubleFactor);//Math.pow(x, y)——计算x的y次方
}
}
}
插值器的内容很简单,只需要实现getInterpolation(float input)这个接口即可,这个接口的输入input是根据当前时间得到的一个值,每次刷新得到的值不同,因此输出也就成了一个动态值。因此我们可以得知插值器的原理——输出是时间输入的函数。知晓这个原理,我们就可以编写自己的插值器。下面的内容是前面出现的getTransformation()中的interpolatedTime,也就是input。从下面的这段代码中,我们可以看见它是和当前时间currentTime相关的。if (mStartTime == -1) { mStartTime = currentTime; } final long startOffset = getStartOffset(); final long duration = mDuration; float normalizedTime; if (duration != 0) { normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) / (float) duration; } else { // time is a step-change with a zero duration normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f; }