属性动画通过改变一个对象的属性值来进行动画,属性动画包含了以下几个特性:
主要用来定义动画的持续时间,默认值为300ms。
指定时间变化的百分比,就是当前流逝时间除以指定的持续时间,这个可以自定义,继承Interpolator,重写getInterpolation方法。
指定动画的执行次数和动画的重复模式
可以把多个动画放到一个集合中,是他们同时执行,或者指定它们直接的顺序和延迟。
可以指定如何去刷新动画的帧,默认是每10ms刷新一次,这个刷新也取决于系统的繁忙程度。
上面我们知道属性动画就是改变对象的属性值来实现动画,ValueAnimator的特点就是你不需要明确的指定你要改变的对象和属性,你只需要得到一个动态的值来自己去设置相应对象的属性,也就是它就是提供属性的变化值,你拿到这个值可以动态的更改对象属性值。总结一句就是监听动画过程,自己实现属性的改变。
举个例子:
// 这里指定了值的变化范围 ValueAnimator animator = ValueAnimator.ofFloat(0, 500); // 这里指定变化持续时间 animator.setDuration(1000); //开始动画 animator.start() //开始动画后,我们可以动态的获取变化值 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //根据变化值来设置imageView对象的Y轴坐标,这样就实现了imageView的垂直移动 imageView.setTranslationY((Float) animation.getAnimatedValue()); } });
上面使用imageView.setTranslationY((Float) animation.getAnimatedValue())
来动态的改变图片的translationY属性,需要说明的是,如果在低版本中,我们使用的是NineOldAnimations这个库,用法跟系统基本一致,在NineOldAnimations里面我们动态改变对象的属性的时候,它提供了一个ViewHelper类,它是设置各种动画值的帮助类,可以简单的设置并应用动画值。所以在3.0以下版本中,使用ViewHelper来进行属性值的改变,上面的设置等同如下:
ViewHelper.setTranslationX(imageView, (Float) animation.getAnimatedValue());
上面的过程如下图所示:
上面的过程应该比较清晰,在上面我们就设置了一个持续时间,下面我们可以来看看我们可以为ValueAnimator设置其他哪些东西。
public ObjectAnimator setDuration(long duration)
设置持续时间public void setEvaluator(TypeEvaluator value)
设置估值器public void setInterpolator(/*Time*/Interpolator value)
设置插值器public void setTarget(Object target)
设置目标对象public void setRepeatCount(int value)
设置动画重复次数public void setRepeatMode(int value)
设置重复模式public void setValues(PropertyValuesHolder... values)
设置值public void setStartDelay(long startDelay)
设置启动延时public static void setFrameDelay(long frameDelay)
设置帧延迟public void setIntValues(int... values)
设置Int值,对应ValueAnimator.ofInt函数public void setFloatValues(float... values)
设置Float值。对应ValueAnimator.ofFloat函数public void setCurrentPlayTime(long playTime)
设置当前执行时间public void setObjectValues(Object... values)
设置Object值,对应ValueAnimator.ofObject函数
上面我们知道ValueAnimator是主要提供一个动态的变化值,这个值是怎么来变化的,它的变化函数就是由估值器和插值器来决定的,我们可以自定义估值器和插值器来自定义值的变化,另外这个变化值的类型,ValueAnimator提供了四种类型,Int,Float,Objcet,PropertyValuesHolder,它囊括了所有的类型。
我们知道,在我们定义一个属性动画对象的时候,可以不需要通过自己来创建的,主要有四种方式:
public static ValueAnimator ofInt(int... values) public static ValueAnimator ofFloat(int... values) public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values)
从上面我们可以看到,不同的方式其实对应的就是不同类型的变化值。第一种方式的变化值类型为Int,第二种方式的变化值类型为Float,第三种方式的变化值类型为Object,第四种方式的变化值类型为PropertyValuesHolder,它其实是一个集合。
对于给定一个范围的值,例如上面例子中ValueAnimator.ofFloat(0, 500),它给定的变化范围为[0, 500],那么在这个范围内到底是如何变化的呢?可以是线性变化,可以是加速变化,可以是减速变化,在内部已经为我们定义好了几种变化方式,我们可以根据情况来进行使用。
首先我们来说说时间插值器和类型估值器
TimeInterpolator中文翻译为时间插值器,它的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有LinearInterpolator
(线性插值器:匀速动画)、AccelerateDecelerateInterpolator
(加速减速插值器:动画两头慢中间快)和DecelerateInterpolator
(减速插值器:动画越来越慢)等;
下面看看线性插值器的源码:
public class LinearInterpolator implements Interpolator { public LinearInterpolator() { } public LinearInterpolator(Context context, AttributeSet attrs) { } public float getInterpolation(float input) { return input; } }
我们自定义插值器的时候,只需要重写getInterpolation方法,其中传入的input参数就是时间流逝的百分比,这个百分比就是当前时间的流逝除以设置的持续时间Duration来得到的。我们实现这个函数的时候,可以通过改变这个值来实现我们想要的效果。
TypeEvaluator的中文翻译为类型估值算法,它的作用是根据当前属性改变的百分比来计算改变后的属性值,系统预置的有IntEvaluator
(针对整型属性)、FloatEvaluator
(针对浮点型属性)和ArgbEvaluator
(针对Color属性)。
我们知道插值器的作用就是返回当前属性改变的百分比,这个百分比我们可以通过重写getInterpolation来自定义。其实真正的变化后的值是从估值器来得到的。
我们来看看IntEvaluator
的源码:
public class IntEvaluator implements TypeEvaluator{ public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int startInt = startValue; return (int)(startInt + fraction * (endValue - startInt)); } }
上述算法很简单,evaluate的三个参数分别表示:估值小数、开始值和结束值,其中的估值小数就是上面getInterpolation的返回值,开始值就是变化值的开始,结束值就是变化值的结束,对应上面例子ValueAnimator.ofFloat(0, 500),开始值为0,结束值为500,通过这三个参数,最终计算出变化后的值,然后将这个值返回去,我们最终得到的就是这个值,然后对指定对象的属性进行设置,这样来实现指定属性值的变化,从而实现了动画效果。
所以我们如果希望自定义变化值的变化快慢,我们需要自定义一个插值器和一个估值器,插值器是为估值器服务的,估值器是为我们服务的,因为它最终返回了变化后的值。
最后,我们如何得到这个变化后的值呢?从上面的例子中我们可以看到,我们只需要使用ValueAnimator的addUpdateListener函数来增加一个更新监听,当这个值变化之后,就会回调onAnimationUpdate函数,在传入的参数ValueAnimator对象中使用getAnimatedValue函数我们就可以获取到变化后的那个值,拿到这个变化后的值之后我们就可以动态的更新对象的属性值了。
还有需要注意的是,我们如果没有显式指定插值器和估值器,它内部有默认值。
/** * 抛物线 * @param view */ public void paowuxian(View view) { ValueAnimator valueAnimator = new ValueAnimator(); valueAnimator.setDuration(3000); //这个地方设置了变化值的类型 valueAnimator.setObjectValues(new PointF(0, 0)); //设置插值器 valueAnimator.setInterpolator(new LinearInterpolator()); //设置估值器 valueAnimator.setEvaluator(new TypeEvaluator() { // fraction = t / duration @Override public PointF evaluate(float fraction, PointF startValue, PointF endValue) { Log.e(TAG, fraction * 3 + ""); // x方向200px/s ,则y方向0.5 * 10 * t PointF point = new PointF(); point.x = 200 * fraction * 3; point.y = 0.5f * 200 * (fraction * 3) * (fraction * 3); //返回变化值 //这个返回值会在addUpdateListener的回调中得到 return point; } }); valueAnimator.start(); valueAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // 得到估值器里面的那个返回值 PointF point = (PointF) animation.getAnimatedValue(); //设置属性值 mBlueBall.setX(point.x); mBlueBall.setY(point.y); } }); }
上面基本说清楚了ValueAnimator的特定和用法,下面来说说如何为这个动画添加事件监听。
ValueAnimator animator = ValueAnimator.ofFloat(); animator.setFloatValues(0, 500); animator.setTarget(imageView); animator.setDuration(1000); animator.start(); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { imageView.setTranslationY((Float) animation.getAnimatedValue()); } }); animator.addListener(new Animator.AnimatorListener(){ @Override public void onAnimationStart(Animator animation) { Log.d(TAG, "onAnimationStart"); } @Override public void onAnimationEnd(Animator animation) { Log.d(TAG, "onAnimationEnd"); } @Override public void onAnimationCancel(Animator animation) { Log.d(TAG, "onAnimationCancel"); } @Override public void onAnimationRepeat(Animator animation) { Log.d(TAG, "onAnimationRepeat"); } });
从上面可以看到直接添加一个监听就可以了,这样就可以监听动画的开始、结束、被取消、重复等事件,上面你需要重写上面四个函数一个都不能少,如果你只需要重写自己需要的函数,那你可以使用AnimatorListenerAdapter
,例如只需要重新onAnimationEnd
函数,因为AnimatorListenerAdapter
继承自AnimatorListener
。