Android Animation API介绍----简单分析ViewAnimation的执行原理

本文详细解析了Android动画系统的核心类Animation的工作原理,包括动画的启动、执行流程、关键方法applyTransformation的作用,以及如何通过getTransformation计算每帧的变换。同时,通过View动画实例展示了动画的驱动机制和执行细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android用于描述动画的抽象基类,Android本身提供的动画和我们自定义的动画一般都继承Animation.

Interpolator & Animation.AnimationListener

Interpolator:用来描述动画运行的过程

Animation.AnimationListener:动画执行过程的回调,目前提供了onAnimationStart,onAnimationEnd,onAnimationRepeat三个基础回调.

Animation定义的styleable

    <declare-styleable name="Animation">
        <attr name="interpolator" />
        <attr name="fillEnabled" format="boolean" />
        <attr name="fillBefore" format="boolean" />
        <attr name="fillAfter" format="boolean" />
        <attr name="duration" />
        <attr name="startOffset" format="integer" />
        <attr name="repeatCount" format="integer">
            <enum name="infinite" value="-1" />
        </attr>
        <attr name="repeatMode">
            <enum name="restart" value="1" />
            <enum name="reverse" value="2" />
        </attr>
        <attr name="zAdjustment">
            <enum name="normal" value="0" />
            <enum name="top" value="1" />
            <enum name="bottom" value="-1" />
        </attr>
        <attr name="background" />
        <attr name="detachWallpaper" format="boolean" />
        <attr name="showWallpaper" format="boolean" />
        <attr name="hasRoundedCorners" format="boolean" />
    </declare-styleable>

我们可以在Style中控制我们Animation的行为.

核心方法applyTransformation

/**
 * Helper for getTransformation. Subclasses should implement this to apply
 * their transforms given an interpolation value.  Implementations of this
 * method should always replace the specified Transformation or document
 * they are doing otherwise.
 *
 * @param interpolatedTime The value of the normalized time (0.0 to 1.0)
 *        after it has been run through the interpolation function.
 * @param t The Transformation object to fill in with the current
 *        transforms.
 */
protected void applyTransformation(float interpolatedTime, Transformation t) {
}

不管是系统提供的动画API还是我们自定义动画,一般都需要重写这个方法,实现需要的动画变换逻辑.

核心方法start/startNow/setStartTime

/**
 * Convenience method to start the animation the first time
 * {@link #getTransformation(long, Transformation)} is invoked.
 */
public void start() {
    setStartTime(-1);
}

/**
 * Convenience method to start the animation at the current time in
 * milliseconds.
 */
public void startNow() {
    setStartTime(AnimationUtils.currentAnimationTimeMillis());
}


/**
 * When this animation should start. When the start time is set to
 * {@link #START_ON_FIRST_FRAME}, the animation will start the first time
 * {@link #getTransformation(long, Transformation)} is invoked. The time passed
 * to this method should be obtained by calling
 * {@link AnimationUtils#currentAnimationTimeMillis()} instead of
 * {@link System#currentTimeMillis()}.
 *
 * @param startTimeMillis the start time in milliseconds
 */
public void setStartTime(long startTimeMillis) {
    mStartTime = startTimeMillis;
    mStarted = mEnded = false;
    mCycleFlip = false;
    mRepeated = 0;
    mMore = true;
}

Animation的start方法仅仅都是调用了setStartTime设置了一些相关的参数,并没有真正的去开始播放动画的逻辑,那么问题来了,谁在驱使着动画的播放?并且从注释中我们可以发现一个有意思的东西,如果说我们设置的startTime=START_ON_FIRST_FRAME(-1),那么当当我们第一次调用getTransformation方法的时候,会再设置一次.那么getTransformation谁来调用呢?getTransformation又是用来干嘛的呢?

/**
 * 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
 */
public boolean getTransformation(long currentTime, Transformation outTransformation) {
    if (mStartTime == -1) {
        mStartTime = currentTime;
    }

    final long startOffset = getStartOffset();//一般情况为0,如果不为0表示动画延迟执行的时间
    final long duration = mDuration;//动画的持续事件
    float normalizedTime;//动画的进度取值在0f-1f之间
    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;//动画持续时间为0的情况,只要到了过了执行的时间,动画立即执行结束进度为1,这里有可能是负数
    }

    final boolean expired = normalizedTime >= 1.0f || isCanceled();//计算动画是否结束
    mMore = !expired;//没结束的话表示需要更多的动画帧

    if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);//mFillEnabled一般为false,仅仅是纠正一下normalizedTime值
//mFillEnabled的值表示计算过程中是否考虑mFillBefore参数的值,ture考虑.默认为false主要考虑mFillAfter的值,,为什么这么说呢?
//看下下面的代码逻辑:mFillEnabled为true的话,normalizedTime为负数的情况下mFillBefore是会被作为参数考虑的.
//对于mFillBefore,mFillAfter我的理解是只有开启mFillEnabled的情况下mFillBefore才会生效,这几个属性仅仅影响的是 是否在动画执行之前或者之后要不要将动画的第一帧或最后一帧动画属性设置给View.
//这直接给我们的体验效果就是动画执行前View就会显示在动画的第一帧的位置,结束的时候并停留在动画结束的位置
    if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
        if (!mStarted) {
            fireAnimationStart();//第一帧开始时回调onAnimationStart方法
            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;
        }

        final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
        applyTransformation(interpolatedTime, outTransformation);//调用applyTransformation方法计算出当前进度对应的outTransformation信息
    }

    if (expired) {
        if (mRepeatCount == mRepeated || isCanceled()) {
            if (!mEnded) {
                mEnded = true;
                guard.close();
                fireAnimationEnd();
            }
        } else {
            if (mRepeatCount > 0) {
                mRepeated++;
            }

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

            mStartTime = -1;
            mMore = true;

            fireAnimationRepeat();//结束的话回调onAnimationEnd方法,不管是正常结束还是被cancel结束都会回调该方法
        }
    }

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

    return mMore;
}

整体的逻辑较为好理解,但是该方法谁来调用呢?

以View动画为例剖析Animation执行的原理

例子:

TextView tv = findViewById(R.id.testtv);
TranslateAnimation translateAnimation = new TranslateAnimation(-100,0,0,1000);
translateAnimation.setDuration(2000);
translateAnimation.setStartOffset(1000);
tv.startAnimation(translateAnimation);

看下View的方法:

public void startAnimation(Animation animation) {
    animation.setStartTime(Animation.START_ON_FIRST_FRAME);
    setAnimation(animation);//将Animation和View绑定
    invalidateParentCaches();
    invalidate(true);//最终其实就是执行ViewRootImpl的scheduleTraversals,注册vsync信号,等待下次vsync刷新UI
}

当vsync信号来的时候,变会执行View的测量布局绘制操作,动画真正执行的地方便是在View的绘制中:

boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
 .........
    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);//执行阶段,获取每一帧的Transformation信息
    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) {//如果动画还需要继续执行,继续invalidate,注册vsync信号,重复上述操作
        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 {
            if (parent.mInvalidateRegion == null) {
                parent.mInvalidateRegion = new RectF();
            }
            final RectF region = parent.mInvalidateRegion;
            a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
                    invalidationTransform);

            // 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 int top = mTop + (int) region.top;
            parent.invalidate(left, top, left + (int) (region.width() + .5f),
                    top + (int) (region.height() + .5f));
        }
    }
    return more;
}

总结

现在在回头看看之前提出的问题:

动画的驱动是什么? getTransformation什么时候调用?在这里View动画驱动是vsync信号,先要通过ViewRootImpl注册vsync信号,然后接收vysnc在view绘制的过程中获取每一帧动画的显示信息并设置到对应的View中.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值