ViewPropertyAnimator概述

来源:互联网 发布:linux mint 内核版本 编辑:程序博客网 时间:2024/06/07 19:06

转载 
http://blog.csdn.net/javazejian/article/details/52381558 
出自【zejian的博客】 

关联文章:

走进绚烂多彩的属性动画-Property Animation(上篇) 
走进绚烂多彩的属性动画-Property Animation之Interpolator和TypeEvaluator(下篇) 
属性动画-Property Animation之ViewPropertyAnimator 你应该知道的一切 
Android布局动画之animateLayoutChanges与LayoutTransition

  原本打算这篇作为属性动画的完结篇,但目前情况来看,估计无法完结,前两天研究了一下ViewPropertyAnimator这个Android 3.1版本后新添加的类,感觉挺有必要用一篇文章来记录一下这个类,ViewPropertyAnimator本身也算不上什么高级类,自然也不是什么特殊技巧,那这个类到底是用来干什么的呢?这就是我们本篇的目的所在啦,接下来我们就来全面地了解一下ViewPropertyAnimator

1.ViewPropertyAnimator概述

  通过前两篇的学习,我们应该明白了属性动画的推出已不再是针对于View而进行设计的了,而是一种对数值不断操作的过程,我们可以将属性动画对数值的操作过程设置到指定对象的属性上来,从而形成一种动画的效果。虽然属性动画给我们提供了ValueAnimator类和ObjectAnimator类,在正常情况下,基本都能满足我们对动画操作的需求,但ValueAnimator类和ObjectAnimator类本身并不是针对View对象的而设计的,而我们在大多数情况下主要都还是对View进行动画操作的,因此Google官方在Android 3.1系统中补充了ViewPropertyAnimator类,这个类便是专门为View动画而设计的。当然这个类不仅仅是为提供View而简单设计的,它存在以下优点: 

  • 专门针对View对象动画而操作的类。
  • 提供了更简洁的链式调用设置多个属性动画,这些动画可以同时进行的。
  • 拥有更好的性能,多个属性动画是一次同时变化,只执行一次UI刷新(也就是只调用一次invalidate,而n个ObjectAnimator就会进行n次属性变化,就有n次invalidate)。
  • 每个属性提供两种类型方法设置。
  • 该类只能通过View的animate()获取其实例对象的引用

  好~,下面我们来了解一下ViewPropertyAnimator常规使用

2.ViewPropertyAnimator常规使用

之前我们要设置一个View控件旋转360的代码是这样:

ObjectAnimator.ofFloat(btn,"rotation",360).setDuration(200).start();
  • 1
  • 1

而现在我们使用ViewPropertyAnimator后是这样:

btn.animate().rotation(360).setDuration(200);
  • 1
  • 1

  代码是不是特简洁?这里我们来解析一下,首先必须用View#animate()方法来获取一个ViewPropertyAnimator的对象实例,前面我们说过ViewPropertyAnimator支持链式操作,所以这里直接通过rotation方法设置旋转角度,再设置时间即可,有没有发现连动画的启动都不用我们去操作!是的,ViewPropertyAnimator内部会自动去调用 
对于View#animate()方法,这里再说明一下,animate()方法是在Android 3.1系统上新增的一个方法,其作用就是返回ViewPropertyAnimator的实例对象,其源码如下,一目了然:

 /** * This method returns a ViewPropertyAnimator object, which can be used to animate * specific properties on this View. * * @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View. */public ViewPropertyAnimator animate() {    if (mAnimator == null) {        mAnimator = new ViewPropertyAnimator(this);    }    return mAnimator;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

接着我们再来试试别的方法,同时设置一组动画集合如下:

AnimatorSet set = new AnimatorSet();set.playTogether( ObjectAnimator.ofFloat(btn,"alpha",0.5f),        ObjectAnimator.ofFloat(btn,"rotation",360),        ObjectAnimator.ofFloat(btn,"scaleX",1.5f),        ObjectAnimator.ofFloat(btn,"scaleY",1.5f),        ObjectAnimator.ofFloat(btn,"translationX",0,50),        ObjectAnimator.ofFloat(btn,"translationY",0,50));set.setDuration(5000).start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

使用ViewPropertyAnimator设置代码如下:

 btn.animate().alpha(0.5f).rotation(360).scaleX(1.5f).scaleY(1.5f)              .translationX(50).translationY(50).setDuration(5000);
  • 1
  • 2
  • 1
  • 2

  是不是已经深深地爱上ViewPropertyAnimator?真的太简洁了!都快感动地哭出来了……先去厕所哭会…….好吧,ViewPropertyAnimator简单用法讲完了,这里小结一下ViewPropertyAnimator的常用方法:

MethodDiscriptionalpha(float value)设置透明度,value表示变化到多少,1不透明,0全透明。scaleY(float value)设置Y轴方向的缩放大小,value表示缩放到多少。1表示正常规格。小于1代表缩小,大于1代表放大。scaleX(float value)设置X轴方向的缩放大小,value表示缩放到多少。1表示正常规格。小于1代表缩小,大于1代表放大。translationY(float value)设置Y轴方向的移动值,作为增量来控制View对象相对于它父容器的左上角坐标偏移的位置,即移动到哪里。translationX(float value)设置X轴方向的移动值,作为增量来控制View对象相对于它父容器的左上角坐标偏移的位置。rotation(float value)控制View对象围绕支点进行旋转, rotation针对2D旋转rotationX (float value)控制View对象围绕X支点进行旋转, rotationX针对3D旋转rotationY(float value)控制View对象围绕Y支点进行旋转, rotationY针对3D旋转x(float value)控制View对象相对于它父容器的左上角坐标在X轴方向的最终位置。y(float value)控制View对象相对于它父容器的左上角坐标在Y轴方向的最终位置void cancel()取消当前正在执行的动画setListener(Animator.AnimatorListener listener)设置监听器,监听动画的开始,结束,取消,重复播放setUpdateListener(ValueAnimator.AnimatorUpdateListener listener)设置监听器,监听动画的每一帧的播放setInterpolator(TimeInterpolator interpolator)设置插值器setStartDelay(long startDelay)设置动画延长开始的时间setDuration(long duration)设置动画执行的时间withLayer()设置是否开启硬件加速withStartAction(Runnable runnable)设置用于动画监听开始(Animator.AnimatorListener)时运行的Runnable任务对象withEndAction(Runnable runnable)设置用于动画监听结束(Animator.AnimatorListener)时运行的Runnable任务对象

  以上便是ViewPropertyAnimator一些操作方法,其实上面很多属性设置方法都对应着一个By结尾的方法,其变量则代表的是变化量,如下: 
 
我们看看其中scaleY与scaleYBy的实现:

public ViewPropertyAnimator scaleY(float value) {        animateProperty(SCALE_Y, value);        return this;    }public ViewPropertyAnimator scaleYBy(float value) {    animatePropertyBy(SCALE_Y, value);    return this;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

再看看animateProperty()与 animatePropertyBy()

 private void animateProperty(int constantName, float toValue) {        float fromValue = getValue(constantName);        float deltaValue = toValue - fromValue;        animatePropertyBy(constantName, fromValue, deltaValue);    } private void animatePropertyBy(int constantName, float byValue) {        float fromValue = getValue(constantName);        animatePropertyBy(constantName, fromValue, byValue);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  看了源码现在应该很清楚有By结尾(代表变化量的大小)和没By结尾(代表变化到多少)的方法的区别了吧。好~,再来看看监听器,实际上我们可以通过setListener(Animator.AnimatorListener listener)setUpdateListener(ValueAnimator.AnimatorUpdateListener listener)设置自定义监听器,而在ViewPropertyAnimator内部也有自己实现的监听器,同样我们可以看一下其实现源码:

private class AnimatorEventListener            implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {        @Override        public void onAnimationStart(Animator animation) {            //调用了设置硬件加速的Runnable            if (mAnimatorSetupMap != null) {                Runnable r = mAnimatorSetupMap.get(animation);                if (r != null) {                    r.run();                }                mAnimatorSetupMap.remove(animation);            }            if (mAnimatorOnStartMap != null) {                  //调用我们通过withStartAction(Runnable runnable)方法设置的runnable                Runnable r = mAnimatorOnStartMap.get(animation);                if (r != null) {                    r.run();                }                mAnimatorOnStartMap.remove(animation);            }            if (mListener != null) {                //调用我们自定义的监听器方法                mListener.onAnimationStart(animation);            }        }        @Override        public void onAnimationCancel(Animator animation) {            if (mListener != null) {                //调用我们自定义的监听器方法                mListener.onAnimationCancel(animation);            }            if (mAnimatorOnEndMap != null) {                mAnimatorOnEndMap.remove(animation);            }        }        @Override        public void onAnimationRepeat(Animator animation) {            if (mListener != null) {                //调用我们自定义的监听器方法                mListener.onAnimationRepeat(animation);            }        }        @Override        public void onAnimationEnd(Animator animation) {            mView.setHasTransientState(false);            if (mListener != null) {                //调用我们自定义的监听器方法                mListener.onAnimationEnd(animation);            }            if (mAnimatorOnEndMap != null) {                  //调用我们通过withEndAction(Runnable runnable)方法设置的runnable                Runnable r = mAnimatorOnEndMap.get(animation);                if (r != null) {                    r.run();                }                mAnimatorOnEndMap.remove(animation);            }            if (mAnimatorCleanupMap != null) {               //移除硬件加速                Runnable r = mAnimatorCleanupMap.get(animation);                if (r != null) {                    r.run();                }                mAnimatorCleanupMap.remove(animation);            }            mAnimatorMap.remove(animation);        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

  由源码我们知道当监听器仅需要监听动画的开始和结束时,我们可以通过withStartAction(Runnable runnable)withEndAction(Runnable runnable)方法来设置一些特殊的监听操作。在AnimatorEventListener中的开始事件还会判断是否开启硬件加速,当然在动画结束时也会去关闭硬件加速。我们可以通过ViewPropertyAnimator #withLayer()方法开启硬件加速功能。到此对于ViewPropertyAnimator的常规使用方式已很清晰了。剩下的我们就来剖析剖析ViewPropertyAnimator内部到底是如何运作的,同时又是如何优化动画性能的。

3.ViewPropertyAnimator原理解析

  我们先通过一副图来大概了解一下ViewPropertyAnimator内部的整体运行工作原理(图太小的话请右键在新页面打开哈,不知为什么markdown限制了大小 。。郁闷中。。): 
 
我们这里先给出整体执行流程(有个整体的概念就行哈,不理解也没有关系,看完下面的分析,再回来来看看也是可以),然后再详细分析:

  • 1.通过imageView.animate()获取ViewPropertyAnimator对象。
  • 2.调用alpha、translationX等方法,返回当前ViewPropertyAnimator对象,可以继续链式调用
  • 3.alpha、translationX等方法内部最终调用animatePropertyBy(int constantName, float startValue, float byValue)方法
  • 4.在animatePropertyBy方法中则会将alpha、translationX等方法的操作封装成NameVauleHolder,并将每个NameValueHolder对象添加到准备列表mPendingAnimations中。
  • 5.animatePropertyBy方法启动mAnimationStarter,调用startAnimation,开始动画。
  • 6.startAnimation方法中会创建一个ValueAnimator对象设置内部监听器AnimatorEventListener,并将mPendingAnimations和要进行动画的属性名称封装成一个PropertyBundle对象,最后mAnimatorMap保存当前Animator和对应的PropertyBundle对象。该Map将会在animatePropertyBy方法和Animator监听器mAnimatorEventListener中使用,启动动画。
  • 7.在动画的监听器的onAnimationUpdate方法中设置所有属性的变化值,并通过RenderNode类优化绘制性能,最后刷新界面。

  有了整体概念后,现在我们沿着该工作流程图的路线来分析ViewPropertyAnimator内部执行过程,从上图可以看出,通过View#animate()获取到ViewPropertyAnimator实例后,可以通过ViewPropertyAnimator提供的多种方法来设置动画,如translationX()、scaleX()等等,而当调用完这些方法后,其内部最终则会通过多次调用animatorPropertyBy(),我们先看看animatePropertyBy方法源码:

/**     * Utility function, called by animateProperty() and animatePropertyBy(), which handles the     * details of adding a pending animation and posting the request to start the animation.     *     * @param constantName The specifier for the property being animated     * @param startValue The starting value of the property     * @param byValue The amount by which the property will change     */    private void animatePropertyBy(int constantName, float startValue, float byValue) {        // First, cancel any existing animations on this property        //判断该属性上是否存在运行的动画,存在则结束。        if (mAnimatorMap.size() > 0) {            Animator animatorToCancel = null;            Set<Animator> animatorSet = mAnimatorMap.keySet();            for (Animator runningAnim : animatorSet) {                PropertyBundle bundle = mAnimatorMap.get(runningAnim);                if (bundle.cancel(constantName)) {// 结束对应属性动画                    // property was canceled - cancel the animation if it's now empty                    // Note that it's safe to break out here because every new animation                    // on a property will cancel a previous animation on that property, so                    // there can only ever be one such animation running.                    if (bundle.mPropertyMask == NONE) {//判断是否还有其他属性                        // the animation is no longer changing anything - cancel it                        animatorToCancel = runningAnim;                        break;                    }                }            }            if (animatorToCancel != null) {                animatorToCancel.cancel();            }        }//将要执行的属性的名称,开始值,变化值封装成NameValuesHolder对象        NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);        //添加到准备列表中        mPendingAnimations.add(nameValuePair);        mView.removeCallbacks(mAnimationStarter);        mView.postOnAnimation(mAnimationStarter);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

  从源码可以看出,animatePropertyBy方法主要干了以下几件事:

  • 首先会去当前属性是否还有在动画在执行,如果有则先结束该属性上的动画,保证该属性上只有一个Animator在进行动画操作。
  • 将本次动画需要执行的动画属性封装成一个NameValueHolder对象
  • 将每个NameValuesHolder对象添加到mPendingAnimations的准备列表中 

  NameValuesHolder对象是一个内部类,其相关信息如下: 
NameValueHolder:内部类,封装每个要进行动画属性值开始值和变化值,比如translationX(200),那么这个动画的属性值、开始值和变化值将被封装成一个NameValueHolder,其源码也非常简单:

static class NameValuesHolder {        int mNameConstant;//要进行动画的属性名称        float mFromValue;//开始值        float mDeltaValue;//变化值        NameValuesHolder(int nameConstant, float fromValue, float deltaValue) {            mNameConstant = nameConstant;            mFromValue = fromValue;            mDeltaValue = deltaValue;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  而mPendingAnimations的相关信息如下: 
mPendingAnimations:装载的是准备进行动画的属性值(NameValueHolder)所有列表,也就是每次要同时进行动画的全部属性的集合

ArrayList<NameValuesHolder> mPendingAnimations = new ArrayList<NameValuesHolder>();
  • 1
  • 1

  当添加完每个要运行的属性动画后,则会通过mAnimationStarter对象去调用startAnimation(),启动动画。 
Runnable mAnimationStarter: 用来执行动画的Runnable。它会执行startAnimation方法,而在startAnimation方法中会通过animator.start()启动动画,源码非常简洁:

private Runnable mAnimationStarter = new Runnable() {        @Override        public void run() {            startAnimation();        }};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

接着我们看看startAnimation()的源码:

/** * Starts the underlying Animator for a set of properties. We use a single animator that * simply runs from 0 to 1, and then use that fractional value to set each property * value accordingly. */private void startAnimation() {    if (mRTBackend != null && mRTBackend.startAnimation(this)) {        return;    }    mView.setHasTransientState(true);    //创建ValueAnimator    ValueAnimator animator = ValueAnimator.ofFloat(1.0f);    //clone一份mPendingAnimations赋值给nameValueList    ArrayList<NameValuesHolder> nameValueList =            (ArrayList<NameValuesHolder>) mPendingAnimations.clone();     //赋值完后清空    mPendingAnimations.clear();    //用于标识要执行动画的属性    int propertyMask = 0;    int propertyCount = nameValueList.size();    //遍历所有nameValuesHolder,取出其属性名称mNameConstant,    //执行"|"操作并最终赋值propertyMask    for (int i = 0; i < propertyCount; ++i) {        NameValuesHolder nameValuesHolder = nameValueList.get(i);        propertyMask |= nameValuesHolder.mNameConstant;    }    //创建PropertyBundle,并添加到mAnimatorMap中    mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));    if (mPendingSetupAction != null) {        //设置硬件加速        mAnimatorSetupMap.put(animator, mPendingSetupAction);        mPendingSetupAction = null;    }    if (mPendingCleanupAction != null) {       //移除硬件加速        mAnimatorCleanupMap.put(animator, mPendingCleanupAction);        mPendingCleanupAction = null;    }    if (mPendingOnStartAction != null) {        //设置开始的动画(监听器的开始方法中调用)        mAnimatorOnStartMap.put(animator, mPendingOnStartAction);        mPendingOnStartAction = null;    }    if (mPendingOnEndAction != null) {        //设置结束后要进行的下一个动画(监听器的结束方法中调用)        mAnimatorOnEndMap.put(animator, mPendingOnEndAction);        mPendingOnEndAction = null;    }    //添加内部监听器    animator.addUpdateListener(mAnimatorEventListener);    animator.addListener(mAnimatorEventListener);    //判断是否延长开始    if (mStartDelaySet) {        animator.setStartDelay(mStartDelay);    }    //执行动画的实现    if (mDurationSet) {        animator.setDuration(mDuration);    }    //设置插值器    if (mInterpolatorSet) {        animator.setInterpolator(mInterpolator);    }    //开始执行动画    animator.start();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

  我们上面的注释非常全面,这里startAnimation主要做下面几件事: 

  • 创建Animator,变化值从0到1,设置内部监听器mAnimatorEventListener。
  • clone一份mPendingAnimations列表,并计算属性值标记propertyMask,封装成PropertyBundle对象。
  • 使用mAnimatorMap保存当前Animator和对应的PropertyBundle对象。该Map将会在animatePropertyBy方法和Animator监听器mAnimatorEventListener中使用。
  • 启动animator动画。


  关于PropertyBundle的分析如下: 
PropertyBundle: 内部类,存放着将要执行的动画的属性集合信息,每次调用animator.start();前,都会将存放在mPendingAnimations的clone一份存入PropertyBundle的内部变量mNameValuesHolder中,然后再将遍历mPendingAnimations中的NameValueHolder类,取出要执行的属性进行”|”操作,最后记录成一个mPropertyMask的变量,存放在PropertyBundle中,PropertyBundle就是最终要执行动画的全部属性的封装类,其内部结构如下图 

AnimatorEventListener: ViewPropertyAnimator内部的监听器。这个类实现了Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener接口。我们前面已经分享过它的部分源码,这个类还有一个onAnimationUpdate()的监听方法,这个方法我们放在后面解析,它是动画执行的关键所在。

HashMap mAnimatorMap: 存放PropertyBundle类的Map。这个Map中存放的是正在执行的动画的PropertyBundle,这个PropertyBundle包含这本次动画的所有属性的信息。最终在AnimatorEventListener的onAnimationUpdate()方法中会通过这个map获取相应的属性,然后不断更新每帧的属性值以达到动画效果。通过前面对animatePropertyBy方法的分析,我们可以知道该Map会保证当前只有一个Animator对象对该View的属性进行操作,不会存在两个Animator在操作同一个属性,其声明如下:

private HashMap<Animator, PropertyBundle> mAnimatorMap =
        new HashMap<Animator, PropertyBundle>();
  • 1
  • 2
  • 1
  • 2

  最后我们看看动画是在哪里执行的,根据我们前面的原理图,内部监听器的onAnimationUpdate()方法将会被调用(当然内部监听器AnimatorEventListener实现了两个动画监听接口,其开始,结束,重复,取消4个方法也会被调用,这个我们前面已分析过)。

@Override        public void onAnimationUpdate(ValueAnimator animation) {        //取出当前Animator对应用propertyBundle对象            PropertyBundle propertyBundle = mAnimatorMap.get(animation);            if (propertyBundle == null) {                // Shouldn't happen, but just to play it safe                return;            }        //是否开启了硬件加速            boolean hardwareAccelerated = mView.isHardwareAccelerated();            // alpha requires slightly different treatment than the other (transform) properties.            // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation            // logic is dependent on how the view handles an internal call to onSetAlpha().            // We track what kinds of properties are set, and how alpha is handled when it is            // set, and perform the invalidation steps appropriately.            boolean alphaHandled = false;            if (!hardwareAccelerated) {                mView.invalidateParentCaches();            }            //取出当前的估算值(插值器计算值)            float fraction = animation.getAnimatedFraction();            int propertyMask = propertyBundle.mPropertyMask;            if ((propertyMask & TRANSFORM_MASK) != 0) {                mView.invalidateViewProperty(hardwareAccelerated, false);            }            //取出所有要执行的属性动画的封装对象NameValuesHolder            ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;            if (valueList != null) {                int count = valueList.size();                //遍历所有NameValuesHolder,计算变化值,并设置给对应的属性                for (int i = 0; i < count; ++i) {                    NameValuesHolder values = valueList.get(i);                    float value = values.mFromValue + fraction * values.mDeltaValue;                    if (values.mNameConstant == ALPHA) {                        alphaHandled = mView.setAlphaNoInvalidation(value);                    } else {                        setValue(values.mNameConstant, value);                    }                }            }            if ((propertyMask & TRANSFORM_MASK) != 0) {                if (!hardwareAccelerated) {                    mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation                }            }            // invalidate(false) in all cases except if alphaHandled gets set to true            // via the call to setAlphaNoInvalidation(), above            if (alphaHandled) {                mView.invalidate(true);            } else {                mView.invalidateViewProperty(false, false);            }            if (mUpdateListener != null) {                mUpdateListener.onAnimationUpdate(animation);            }        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

  onAnimationUpdate方法主要做了以下几件事: 

  • 取出当前Animator对应用propertyBundle对象并获取当前的估算值(插值器计算值),用于后续动画属性值的计算
  • 从propertyBundle取出要进行动画的属性列表 ArrayList<NameValuesHolder> valueList
  • 遍历所有NameValuesHolder,计算变化值,并通过setValue设置给对应的属性,如果是ALPHA,则会特殊处理一下,最终形成动画效果 

setValue方法源码:

 private void setValue(int propertyConstant, float value) {        final View.TransformationInfo info = mView.mTransformationInfo;        final RenderNode renderNode = mView.mRenderNode;        switch (propertyConstant) {            case TRANSLATION_X:                renderNode.setTranslationX(value);                break;            case TRANSLATION_Y:                renderNode.setTranslationY(value);                break;            case TRANSLATION_Z:                renderNode.setTranslationZ(value);                break;            case ROTATION:                renderNode.setRotation(value);                break;            case ROTATION_X:                renderNode.setRotationX(value);                break;            case ROTATION_Y:                renderNode.setRotationY(value);                break;            case SCALE_X:                renderNode.setScaleX(value);                break;            case SCALE_Y:                renderNode.setScaleY(value);                break;            case X:                renderNode.setTranslationX(value - mView.mLeft);                break;            case Y:                renderNode.setTranslationY(value - mView.mTop);                break;            case Z:                renderNode.setTranslationZ(value - renderNode.getElevation());                break;            case ALPHA:                info.mAlpha = value;                renderNode.setAlpha(value);                break;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

  从源码可以看出实际上都会把属性值的改变设置到renderNode对象中,而RenderNode类则是一个可以优化绘制流程和绘制动画的类,该类可以提升优化绘制的性能,其内部操作最终会去调用到Native层方法,这里我们就不深追了。 
  最后这里我们再回忆一下前面给出的整体流程说明:

  • 1.通过imageView.animate()获取ViewPropertyAnimator对象。
  • 2.调用alpha、translationX等方法,返回当前ViewPropertyAnimator对象,可以继续链式调用
  • 3.alpha、translationX等方法内部最终调用animatePropertyBy(int constantName, float startValue, float byValue)方法
  • 4.在animatePropertyBy方法中则会将alpha、translationX等方法的操作封装成NameVauleHolder,并将每个NameValueHolder对象添加到准备列表mPendingAnimations中。
  • 5.animatePropertyBy方法启动mAnimationStarter,调用startAnimation,开始动画。
  • 6.startAnimation方法中会创建一个ValueAnimator对象设置内部监听器AnimatorEventListener,并将mPendingAnimations和要进行动画的属性名称封装成一个PropertyBundle对象,最后mAnimatorMap保存当前Animator和对应的PropertyBundle对象。该Map将会在animatePropertyBy方法和Animator监听器mAnimatorEventListener中使用,启动动画。
  • 7.在动画的监听器的onAnimationUpdate方法中设置所有属性的变化值,并通过RenderNode类优化绘制性能,最后刷新界面。

  现在应该比较清晰了吧,以上就是ViewPropertyAnimator内部的大概执行流程。好~,ViewPropertyAnimator介绍到这。

关联文章: 
走进绚烂多彩的属性动画-Property Animation(上篇) 
走进绚烂多彩的属性动画-Property Animation之Interpolator和TypeEvaluator(下篇) 
属性动画-Property Animation之ViewPropertyAnimator 你应该知道的一切

0 0