🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# Tween Animation ## 补间动画的使用 ``` Animation translateAnimation = new TranslateAnimation(0, 100, 0, 0); translateAnimation.setDuration(500); translateAnimation.setInterpolator(new AccelerateInterpolator()); translateAnimation.setFillAfter(true);//设置动画结束后保持当前的位置(即不返回到动画开始前的位置) imageView.startAnimation(translateAnimation); ``` ## TweenAnimation流程图 ![](../images/TweenAnimation流程图.png) ## 补间动画代码的执行过程 ### start View: ``` public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { //部分代码省略 public void startAnimation(Animation animation) { animation.setStartTime(Animation.START_ON_FIRST_FRAME); setAnimation(animation); invalidateParentCaches(); invalidate(true); } void invalidate(boolean invalidateCache) { invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); } void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) { if (mGhostView != null) { mGhostView.invalidate(true); return; } if (skipInvalidate()) { return; } if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || (fullInvalidate && isOpaque() != mLastIsOpaque)) { //部分代码省略 // Propagate the damage rectangle to the parent view. final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; if (p != null && ai != null && l < r && t < b) { final Rect damage = ai.mTmpInvalRect; damage.set(l, t, r, b); //执行ViewParent的invalidateChild方法 p.invalidateChild(this, damage); } //部分代码省略 } } } ``` ViewGroup ``` public abstract class ViewGroup extends View implements ViewParent, ViewManager { /***部分代码省略***/ public final void invalidateChild(View child, final Rect dirty) { ViewParent parent = this; final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { /***部分代码省略***/ do { /***部分代码省略***/ //向顶部的View便利找到根View,即:ViewRootImpl //执行ViewRootImpl的invalidateChildInParent方法 parent = parent.invalidateChildInParent(location, dirty); if (view != null) { // Account for transform on current parent Matrix m = view.getMatrix(); if (!m.isIdentity()) { RectF boundingRect = attachInfo.mTmpTransformRect; boundingRect.set(dirty); m.mapRect(boundingRect); dirty.set((int) (boundingRect.left - 0.5f), (int) (boundingRect.top - 0.5f), (int) (boundingRect.right + 0.5f), (int) (boundingRect.bottom + 0.5f)); } } /***部分代码省略***/ } while (parent != null); } } } ``` ViewRootImpl ``` public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { /***部分代码省略***/ @Override public ViewParent invalidateChildInParent(int[] location, Rect dirty) { checkThread(); /***部分代码省略***/ invalidateRectOnScreen(dirty); return null; } private void invalidateRectOnScreen(Rect dirty) { /***部分代码省略***/ if (!mWillDrawSoon && (intersected || mIsAnimating)) { //开始View的绘制任务 scheduleTraversals(); } } } ``` 之前写过一篇文章 [ViewRootImpl的独白,我不是一个View(布局篇)](http://dandanlove.com/2017/12/11/viewrootimpl-activity/) 其中 [ViewRootImpl对mView进行操作](http://dandanlove.com/2017/12/11/viewrootimpl-activity/#ViewRootImpl%E5%AF%B9mView%E8%BF%9B%E8%A1%8C%E6%93%8D%E4%BD%9C) 讲述了再`ViewRootImpl` 中View的绘制。 ### draw ``` public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { //部分代码省略 public void draw(Canvas canvas) { /***部分代码省略***/ //如果有子 View(DecorView当然有子View),就会调用dispatchDraw() 将绘制事件通知给子 View。 //ViewGroup 重写了 dispatchDraw(),调用了 drawChild() //drawChild() 调用了子 View 的 draw(Canvas, ViewGroup, long) } boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) { final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated(); /***部分代码省略***/ Transformation transformToApply = null; boolean concatMatrix = false; final boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired; final Animation a = getAnimation(); if (a != null) { more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired); concatMatrix = a.willChangeTransformationMatrix(); if (concatMatrix) { mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM; } transformToApply = parent.getChildTransformation(); } else { /***部分代码省略***/ } /***部分代码省略***/ } private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime, Animation a, boolean scalingRequired) { /***部分代码省略***/ //绘制动画的当前帧,并获取当前动画的状态(是否继续运行) boolean more = a.getTransformation(drawingTime, t, 1f); if (scalingRequired && mAttachInfo.mApplicationScale != 1f) { if (parent.mInvalidationTransformation == null) { parent.mInvalidationTransformation = new Transformation(); } invalidationTransform = parent.mInvalidationTransformation; a.getTransformation(drawingTime, invalidationTransform, 1f); } else { invalidationTransform = t; } //如果动画没有结果 if (more) { if (!a.willChangeBounds()) { 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); } } else { /***部分代码省略***/ //进行绘制 parent.invalidate(left, top, left + (int) (region.width() + .5f), top + (int) (region.height() + .5f)); } } return more; } } ``` ### running ``` public abstract class Animation implements Cloneable { /***部分代码省略***/ public boolean getTransformation(long currentTime, Transformation outTransformation) { /***部分代码省略***/ //执行时间是否过期 final boolean expired = normalizedTime >= 1.0f; mMore = !expired; //动画进度为0.0~1.0之间 if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) { /***部分代码省略***/ //插值器计算动画执行进度 final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime); //真正的动画效果代码执行处(通过矩阵变化) applyTransformation(interpolatedTime, outTransformation); } //如果动画绘制完成 if (expired) { //判断动画是否需要继续循环 if (mRepeatCount == mRepeated) { if (!mEnded) { mEnded = true; guard.close(); fireAnimationEnd(); } } else { if (mRepeatCount > 0) { mRepeated++; } if (mRepeatMode == REVERSE) { mCycleFlip = !mCycleFlip; } mStartTime = -1; mMore = true; fireAnimationRepeat(); } } if (!mMore && mOneMoreTime) { mOneMoreTime = false; return true; } return mMore; } } ``` ## 其他 通过代码分析可以证明补间动画也不会存在内存泄露的问题,因为他是靠着View的绘制来完成每一帧动效的展示。