# Tween Animation
## 補間動畫的使用
```
Animation translateAnimation = new TranslateAnimation(0, 100, 0, 0);
translateAnimation.setDuration(500);
translateAnimation.setInterpolator(new AccelerateInterpolator());
translateAnimation.setFillAfter(true);//設置動畫結束后保持當前的位置(即不返回到動畫開始前的位置)
imageView.startAnimation(translateAnimation);
```
## TweenAnimation流程圖

## 補間動畫代碼的執行過程
### 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