Java属性动画详解

本文详细探讨了Java属性动画的实现原理,从ObjectAnimator的start()函数开始,逐步分析了ValueAnimator的状态管理、初始化过程、关键帧设置、Evaluator与Interpolator的作用。通过对ofInt()方法的剖析,揭示了如何构建和配置动画,以及动画值的计算和更新。通过对动画流程的跟踪,揭示了动画如何与UI系统交互,执行每一帧的更新。
摘要由CSDN通过智能技术生成

本文假定你已经对属性动画有了一定的了解,至少使用过属性动画。下面我们就从属性动画最简单的使用开始。

    ObjectAnimator
      .ofInt(target,propName,values[])
      .setInterpolator(LinearInterpolator)
      .setEvaluator(IntEvaluator)
      .setDuration(500)
      .start();

相信这段代码对你一定不陌生,代码中有几个地方是本文中将要重点关注的,setInterpolator(...)setEvaluator(...)setDuration(...)在源代码中是如何被使用的。另外,我们也将重点关注Android中属性动画是如何一步步地实现动画效果的(精确到每一帧(frame))。最后啰嗦几句,本文中使用的代码是Android 4.2.2。

上面代码的作用就是生成一个属性动画,根据ofInt()我们知道只是一个属性值类型为Int的View的动画。先放过其他的函数,从ObjectAnimator的start()函数开始。

    public void  start() {
        //...省略不必要代码 
        super.start();
    }

从代码中我们知道,它调用了父类的start()方法,也就是ValueAnimator.start()。这个方法内部又调用了自身类内部的start(boolean playBackwards)方法。

    /**
     * 方法简要介绍:
     * 这个方法开始播放动画。这个start()方法使用一个boolean值playBackwards来判断是否需
     * 要回放动画。这个值通常为false,但当它从reverse()方法被调用的时候也
     * 可能为true。有一点需要注意的是,这个方法必须从UI主线程调用。
     */
    private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        mPlayingBackwards = playBackwards;
        mCurrentIteration = 0;
        mPlayingState = STOPPED;
        mStarted = true;
        mStartedDelay = false;
        AnimationHandler animationHandler = getOrCreateAnimationHandler();
        animationHandler.mPendingAnimations.add(this);
        if (mStartDelay == 0) {
            // 在动画实际运行前,设置动画的初始值
            setCurrentPlayTime(0);
            mPlayingState = STOPPED;
            mRunning = true;
            notifyStartListeners();
        }
        animationHandler.start();
    }

对代码中几个值的解释

  • mPlayingStated代表当前动画的状态。用于找出什么时候开始动画(if state == STOPPED)。当然也用于在animator被调用了cancel()或 end()在动画的最后一帧停止它。可能的值为STOPPED, RUNNING, SEEKED.
  • mStarted是Animator中一个额外用于标识播放状态的值,用来指示这个动画是否需要延时执行。
  • mStartedDelay指示这个动画是否已经从startDelay中开始执行。
  • AnimationHandler animationHandler 是一个实现了Runnable接口的ValueAnimator内部类,暂时先放过,后面我们会具体谈到。

从上面这段代码中,我们了解到一个ValueAnimator有它自己的状态(STOPPED, RUNNING, SEEKED),另外是否延时也影响ValueAnimator的执行。代码的最后调用了animationHandler.start(),看来动画就是从这里启动的。别急,我们还没初始化ValueAnimator呢,跟进setCurrentPlayTime(0)看看。

    public void setCurrentPlayTime(long playTime) {
        initAnimation();
        long currentTime = AnimationUtils.currentAnimationTimeMillis();
        if (mPlayingState != RUNNING) {
            mSeekTime = playTime;
            mPlayingState = SEEKED;
        }
        mStartTime = currentTime - playTime;
        doAnimationFrame(currentTime);
    }

这个函数在animation开始前,设置它的初始值。这个函数用于设置animation进度为指定时间点。playTime应该介于0到animation的总时间之间,包括animation重复执行的时候。如果animation还没有开始,那么它会等到被设置这个时间后才开始。如果animation已经运行,那么setCurrentTime()会将当前的进度设置为这个值,然后从这个点继续播放。

接下来让我们看看initAnimation()

    void initAnimation() {
        if (!mInitialized) {
            int numValues = mValues.length;
            for (int i = 0; i < numValues; ++i) {
                mValues[i].init();
            }
            mInitialized = true;
        }
    }

这个函数一看就觉得跟初始化动画有关。这个函数在处理动画的第一帧前就会被调用。如果startDelay不为0,这个函数就会在就会在延时结束后调用。它完成animation最终的初始化。

那么mValues是什么呢?还记得我们在文章的开头介绍ObjectAnimator的使用吧?还有一个ofInt(T target, Property property, int... values)方法没有介绍。官方文档中对这个方法的解释是:构造并返回一个在int类型的values数值之间ObjectAnimator对象。当values只有一个值的时候,这个值就作为animator的终点值。如果有两个值的话,那么这两个值就作为开始值和结束值。如果有超过两个以上的值的话,那么这些值就作为开始值,作为animator运行的中间值,以及结束值。这些值将均匀地分配到animator的持续时间。

先中断ObjectAnimator.start()流程的分析,回到开头ObjectAnimator.ofInt(...)

接下来让我们深入ofInt(...)的内部看看。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值