# 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的绘制来完成每一帧动效的展示。
- 写在前面的话
- Java
- 基础
- Double的比较
- 小数怎么用二进制表示
- 多线程
- 并发和并行
- 线程池
- 线程池背景
- 线程池构造
- 任务阻塞队列
- Flutter
- 基础知识
- Dart基础
- Android
- 项目架构
- View
- 非UI线程更新View
- AlarmManager
- 对比postDelaryed和Timer
- Bitmap
- 加载100M的图片却不撑爆内存
- Bitmap压缩
- Bitmap局部解码
- 计算图片的内存占用
- Android动画
- Android动画类型
- Android动画原理
- 属性动画
- 帧动画
- 补间动画
- 使用动画的注意事项
- Android新特性
- 权限组
- Android23(Marshmallow)-6.0
- Android24(Nougat)-7.0
- Android26(Oreo)-8.0
- Android28(Pie)-9.0
- Android29(Q)-10.0
- AndroidX迁移
- Kotlin
- 关键字
- Kotlin操作符
- CoroutineScope
- Flow
- CoroutineException