View动画的实现原理

37 阅读2分钟

View动画的简单使用:

val button = findViewById<Button>(R.id.translation_button)
button.setOnClickListener {
    val translationAnimation = AlphaAnimation(1f, 0f)
    translationAnimation.duration = 4000
    it.startAnimation(translationAnimation)
}

这是个很简单的透明度动画从1.0f到0.0f的。 下面我们来分析下源码:

public void startAnimation(Animation animation) {
    animation.setStartTime(Animation.START_ON_FIRST_FRAME);
    setAnimation(animation);
    invalidateParentCaches();
    invalidate(true);
}
```
先是设置调用setAnimation方法给自己设置了一个Animation对象,

````
public void setAnimation(Animation animation) {
    mCurrentAnimation = animation;

    if (animation != null) {
        // If the screen is off assume the animation start time is now instead of
        // the next frame we draw. Keeping the START_ON_FIRST_FRAME start time
        // would cause the animation to start when the screen turns back on
        if (mAttachInfo != null && mAttachInfo.mDisplayState == Display.STATE_OFF
                && animation.getStartTime() == Animation.START_ON_FIRST_FRAME) {
            animation.setStartTime(AnimationUtils.currentAnimationTimeMillis());
        }
        animation.reset();
    }
}
````
其实就是给mCurrentAnimation成员变量赋值,以后有用。
然后调用invalidate方法来重绘自己,这点流程就是通过ViewRootImpl调用scheduleTraversals()发起一个重绘请求,通过Choreographer发送一个异步消息,同时在Choregrapher中处理消息,最终会调用performTraversals()执行重绘。最终会调用View的draw方法进行绘制,

````
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
    ...
    boolean concatMatrix = false;
    final boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired;
    //得到前面赋值的Animation,也就是我们需要执行的动画
    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();
     
````
接着看:
```
private boolean applyLegacyAnimation(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);
        if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler);
        onAnimationStart(); //回调接口的
    }

    final Transformation t = parent.getChildTransformation();
    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;
    }

     
    return more;
}

```这个类在我们的AlphaAnimation中没有实现,这个是Animation的一个方法
public boolean getTransformation(long currentTime, Transformation outTransformation,
        float scale) {
    mScaleFactor = scale;
    return getTransformation(currentTime, outTransformation);
}
```
```
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 || isCanceled();
    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) {
        //若当前为第一帧动画,会触发mListener.onAnimationStart()
            fireAnimationStart();
            mStarted = true;
            if (NoImagePreloadHolder.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;
        }
        //接着看下面的详细方法
        getTransformationAt(normalizedTime, outTransformation);
    }

    if (expired) { //是否动画执行完成
        if (mRepeatCount == mRepeated || isCanceled()) { //是否重复执行动画
            if (!mEnded) {
                mEnded = true;
                guard.close();
                //触发mListener.onAnimationEnd
                fireAnimationEnd();
            }
        } else {
            if (mRepeatCount > 0) {
                mRepeated++;
            }

            if (mRepeatMode == REVERSE) {
                mCycleFlip = !mCycleFlip;
            }

            mStartTime = -1;
            mMore = true;
          //触发mListener.onAnimationRepeated
            fireAnimationRepeat();
        }
    }

    if (!mMore && mOneMoreTime) {
        mOneMoreTime = false;
        return true;
    }

    return mMore;
}
```
```
getTransformationAt(normalizedTime, outTransformation);看下这个方法我们
```
public void getTransformationAt(float normalizedTime, Transformation outTransformation) {
//根据插值器mInterpolator调整动画进度
    final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
    //执行动画
    applyTransformation(interpolatedTime, outTransformation);
}
```
applyTransformation(interpolatedTime, outTransformation);这个方法在Animation是个空方法,具体交给子类去实现,我们看下AlphaAnimation的
```
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    final float alpha = mFromAlpha;
    t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
}
```
每一帧对应一个一个透明度,这样就实现了动画。