前言:唯有脚踏实地,才能厚积薄发,未来只属于为梦想而奋斗的人们,今天的你决定未来的自己。
上一篇给大家介绍了ValueAnimator的大部分函数的用法,不过还都是些简单的用法,这篇我们带大家来看看有关加速器、animator和keyFrame的知识。
下面我们就来看一下利用ValueAnimator是如何实现的。布局和上一篇的都一样,这里就不再细讲了,我们着重来看一下,当点击start anim按钮以后是怎么做的,代码如下:
ValueAnimator animator = ValueAnimator.ofInt(0,600);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int curValue = (int)animation.getAnimatedValue(); tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight()); }});animator.setDuration(1000);animator.setInterpolator(new BounceInterpolator());animator.start();如果大家已经看懂了上篇,这段代码就非常容易理解了,在监听中,我们只改变textview的top和bottom的位置,让它跟着当前动画的值来改变它当前的top和bottom的位置。然后我们利用setDuration(1000)给它设置上做一次动画所需要的时长,然后通过setInterpolator()给它设置插值器,也就是过渡值变化的规则;
public class LinearInterpolator implements Interpolator { public LinearInterpolator() { } public LinearInterpolator(Context context, AttributeSet attrs) { } public float getInterpolation(float input) { return input; }}public interface Interpolator extends TimeInterpolator {}LinearInterpolator实现了Interpolator接口;而Interpolator接口则直接继承自TimeInterpolator,而且并没有添加任何其它的方法。
/** * A time interpolator defines the rate of change of an animation. This allows animations * to have non-linear motion, such as acceleration and deceleration. */public interface TimeInterpolator { /** * Maps a value representing the elapsed fraction of an animation to a value that represents * the interpolated fraction. This interpolated value is then multiplied by the change in * value of an animation to derive the animated value at the current elapsed animation time. * * @param input A value between 0 and 1.0 indicating our current point * in the animation where 0 represents the start and 1.0 represents * the end * @return The interpolation value. This value can be more than 1.0 for * interpolators which overshoot their targets, or less than 0 for * interpolators that undershoot their targets. */ float getInterpolation(float input);}这里是TimeInterpolator的代码,它里面只有一个函数float getInterpolation(float input);我们来讲讲这个函数是干什么的。
ValueAnimator anim = ValueAnimator.ofInt(100, 400); anim.setDuration(1000); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float currentValue = (float) animation.getAnimatedValue(); Log.d("TAG", "cuurent value is " + currentValue); } }); anim.start();我们知道,在我们添加了AnimatorUpdateListener的监听以后,通过在监听函数中调用 animation.getAnimatedValue()就可以得到当前的值;
public class LinearInterpolator implements Interpolator { ………… public float getInterpolation(float input) { return input; }}从上面可以看到,LinearInterpolator在getInterpolation函数中,直接把input值返回,即以当前动画的进度做为动画的数值进度,这也就表示当前动画的数值进度与动画的时间进度一致,比如,如果当前动画进度为0,那动画的数值进度也是0,那如果动画进度为0.5,那动画的数值进度也是在0.5,当动画结束,动画的进度就变成1了,而动画的数值进度也是1了。
public class MyInterploator implements TimeInterpolator { @Override public float getInterpolation(float input) { return 1-input; }}在getInterpolation函数中,我们将进度反转过来,当传0的时候,我们让它数值进度在完成的位置,当完成的时候,我们让它在开始的位置
ValueAnimator animator = ValueAnimator.ofInt(0,600);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int curValue = (int)animation.getAnimatedValue(); tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight()); }});animator.setDuration(1000);animator.setInterpolator(new MyInterploator());animator.start();这里使用自定义插值器的方法与使用普通插值器的方法是完全一样的,下面来看看效果:
从效果图中可见,我们将数值进度倒序返回——即随着动画进度的推进,动画的数值进度从结束位置进行到起始位置;
到这里,想必大家应该已经理解了getInterpolation(float input)函数中input参数与返回值的关系,在重写插值器时,需要强有力的数学知识做基础,一般而言,都是通过数学公式来计算插值器的变化趋势的,大家可以再分析分析其它几个插值器的写法;可以把它他们总结成公式,放到公式画图软件里,看看对应的数学图在(0,1)之间的走向,这个走向就是插值器在数值变化时的样子。
源码在文章底部给出
这幅图讲述了从定义动画的数字区间到通过AnimatorUpdateListener中得到当前动画所对应数值的整个过程。下面我们对这四个步骤具体讲解一下:
(1)、ofInt(0,400)表示指定动画的数字区间,是从0运动到400;
(2)、加速器:上面我们讲了,在动画开始后,通过加速器会返回当前动画进度所对应的数字进度,但这个数字进度是百分制的,以小数表示,如0.2
(3)、Evaluator:我们知道我们通过监听器拿到的是当前动画所对应的具体数值,而不是百分制的进度。那么就必须有一个地方会根据当前的数字进度,将其转化为对应的数值,这个地方就是Evaluator;Evaluator就是将从加速器返回的数字进度转成对应的数字值。所以上部分中,我们讲到的公式:
当前的值 = 100 + (400 - 100)* 显示进度这个公式就是在Evaluator计算的;在拿到当前数字进度所对应的值以后,将其返回
ValueAnimator animator = ValueAnimator.ofInt(0,600);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int curValue = (int)animation.getAnimatedValue(); tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight()); }});animator.setDuration(1000);animator.setEvaluator(new IntEvaluator());animator.setInterpolator(new BounceInterpolator());animator.start();但大家会说了,在此之前,我们在使用ofInt()时,从来没有给它定义过使用IntEvaluator来转换值啊,那怎么也能正常运行呢?因为ofInt和ofFloat都是系统直接提供的函数,所以在使用时都会有默认的加速器和Evaluator来使用的,不指定则使用默认的;对于Evaluator而言,ofInt()的默认Evaluator当然是IntEvaluator;而FloatEvalutar默认的则是FloatEvalutor;
/** * This evaluator can be used to perform type interpolation between可以看到在IntEvaluator中只有一个函数(float fraction, Integer startValue, Integer endValue) ;int
values. */public class IntEvaluator implements TypeEvaluator{ /** * This function returns the result of linearly interpolating the start and end values, with * fraction
representing the proportion between the start and end values. The * calculation is a simple parametric calculation:result = x0 + t * (v1 - v0)
, * wherex0
isstartValue
,x1
isendValue
, * andt
isfraction
. * * @param fraction The fraction from the starting to the ending values * @param startValue The start value; should be of typeint
or *Integer
* @param endValue The end value; should be of typeint
orInteger
* @return A linear interpolation between the start and end values, given the *fraction
parameter. */ public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int startInt = startValue; return (int)(startInt + fraction * (endValue - startInt)); }}
return (int)(startInt + fraction * (endValue - startInt));大家对这个公式是否似曾相识?我们前面提到的公式:
当前的值 = 100 + (400 - 100)* 显示进度是不是与这个公式完全一样?是的,绝逼完全一样啊,计算原理我们在上面已经讲过了,而且根据进度来计算当前数字值本来就是这么算的……
public class MyEvaluator implements TypeEvaluator这里涉及到泛型的概念,不理解的同学可以去看看 《夯实JAVA基本之一 —— 泛型详解(1):基本使用》{ @Override public Integer evaluate(float fraction, Integer startValue, Integer endValue) { return null; }}
public class MyEvaluator implements TypeEvaluator我们在IntEvaluator的基础上修改了下,让它返回值时增加了200;所以当我们定义的区间是ofInt(0,400)时,它的实际返回值区间应该是(200,600){ @Override public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int startInt = startValue; return (int)(200+startInt + fraction * (endValue - startInt)); }}
ValueAnimator animator = ValueAnimator.ofInt(0,400);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int curValue = (int)animation.getAnimatedValue(); tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight()); }});animator.setDuration(1000);animator.setEvaluator(new MyEvaluator());animator.start();设置MyEvaluator前的动画效果:
然后再看看我们设置了MyEvaluator以后的效果:
很明显,textview的动画位置都向下移动了200个点;
再重新看一下下面的这个流程图:
在加速器中,我们可以通过自定义加速器的返回的数值进度来改变返回数值的位置。比如上面我们实现的倒序动画
在Evaluator中,我们又可以通过改变进度值所对应的具体数字来改变数值的位置。
所以,结论来了:
我们可以通过重写加速器改变数值进度来改变数值位置,也可以通过改变Evaluator中进度所对应的数值来改变数值位置。
源码在文章底部给出
下面我们就只通过重写Evaluator来实现数值的倒序输出;public class ReverseEvaluator implements TypeEvaluator其中 fraction * (endValue - startInt)表示动画实际运动的距离,我们用endValue减去实际运动的距离就表示随着运动距离的增加,离终点越来越远,这也就实现了从终点出发,最终运动到起点的效果了。{ @Override public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int startInt = startValue; return (int) (endValue - fraction * (endValue - startInt)); }}
ValueAnimator animator = ValueAnimator.ofInt(0,400);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int curValue = (int)animation.getAnimatedValue(); tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight()); }});animator.setDuration(1000);animator.setEvaluator(new ReverseEvaluator());animator.start();效果图:
源码在文章底部给出
public class ArgbEvaluator implements TypeEvaluator { public Object evaluate(float fraction, Object startValue, Object endValue) { int startInt = (Integer) startValue; int startA = (startInt >> 24); int startR = (startInt >> 16) & 0xff; int startG = (startInt >> 8) & 0xff; int startB = startInt & 0xff; int endInt = (Integer) endValue; int endA = (endInt >> 24); int endR = (endInt >> 16) & 0xff; int endG = (endInt >> 8) & 0xff; int endB = endInt & 0xff; return (int)((startA + (int)(fraction * (endA - startA))) 我们在这里关注两个地方,第一返回值是int类型,这说明我们可以使用ofInt()来初始化动画数值范围。第二:颜色值包括A,R,G,B四个值,每个值是8位所以用16进制表示一个颜色值应该是0xffff0000(纯红色)
下面我们就使用一下ArgbEvaluator,并看看效果:ValueAnimator animator = ValueAnimator.ofInt(0xffffff00,0xff0000ff);animator.setEvaluator(new ArgbEvaluator());animator.setDuration(3000);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int curValue = (int)animation.getAnimatedValue(); tv.setBackgroundColor(curValue); }});animator.start();在这段代码中,我们将动画的数据范围定义为(0xffffff00,0xff0000ff),即从黄色,变为蓝色。
在监听中,我们根据当前传回来的颜色值,将其设置为textview的背景色
我们来看一下效果:
源码在文章底部给出
到这里,我们就已经知道ArgbEvalutor的使用方法和效果了,下面我们再来回头看看ArgbEvalutor的实现方法2、ArgbEvalutor的实现原理
先重新看源码:
/** * This evaluator can be used to perform type interpolation between integer * values that represent ARGB colors. */public class ArgbEvaluator implements TypeEvaluator { /** * This function returns the calculated in-between value for a color * given integers that represent the start and end values in the four * bytes of the 32-bit int. Each channel is separately linearly interpolated * and the resulting calculated values are recombined into the return value. * * @param fraction The fraction from the starting to the ending values * @param startValue A 32-bit int value representing colors in the * separate bytes of the parameter * @param endValue A 32-bit int value representing colors in the * separate bytes of the parameter * @return A value that is calculated to be the linearly interpolated * result, derived by separating the start and end values into separate * color channels and interpolating each one separately, recombining the * resulting values in the same way. */ public Object evaluate(float fraction, Object startValue, Object endValue) { int startInt = (Integer) startValue; int startA = (startInt >> 24); int startR = (startInt >> 16) & 0xff; int startG = (startInt >> 8) & 0xff; int startB = startInt & 0xff; int endInt = (Integer) endValue; int endA = (endInt >> 24); int endR = (endInt >> 16) & 0xff; int endG = (endInt >> 8) & 0xff; int endB = endInt & 0xff; return (int)((startA + (int)(fraction * (endA - startA))) 英文注释的那一坨大家有兴趣,可以看已看看,我这里就直接讲代码了
这段代码分为三部分,第一部分根据startValue求出其中A,R,G,B中各个色彩的初始值;第二部分根据endValue求出其中A,R,G,B中各个色彩的结束值,最后是根据当前动画的百分比进度求出对应的数值
我们先来看第一部分:根据startValue求出其中A,R,G,B中各个色彩的初始值int startInt = (Integer) startValue;int startA = (startInt >> 24);int startR = (startInt >> 16) & 0xff;int startG = (startInt >> 8) & 0xff;int startB = startInt & 0xff;这段代码就是根据位移和与运算求出颜色值中A,R,G,B各个部分对应的值;颜色值与ARGB值的对应关系如下:
所以我们的初始值是0xffffff00,那么求出来的startA = 0xff,startR = oxff,startG = 0xff,startB = 0x00;
关于通过位移和与运算如何得到指定位的值的问题,我就不再讲了,大家如果不理解,可以搜一下相关运算符使用方法的文章。
同样,我们看看第二部分根据endValue求出其中A,R,G,B中各个色彩的结束值:int endInt = (Integer) endValue;int endA = (endInt >> 24);int endR = (endInt >> 16) & 0xff;int endG = (endInt >> 8) & 0xff;int endB = endInt & 0xff;原理与startValue求A,R,G,B对应值的一样,所以对于我们上面例子中初始值ofInt(0xffffff00,0xff0000ff)中的endValue:0xff0000ff所对应的endA = 0xff,endR = ox00;endG = 0x00;endB = 0xff;
最后一部分到了,就是如何根据进度来求得变化的值,我们先看看下面这句是什么意思:startA + (int)(fraction * (endA - startA)))对于这个公式大家应该很容易理解,与IntEvaluator中的计算公式一样,就是根据透明度A的初始值、结束值求得当前进度下透明度A应该的数值。
同理
startR + (int)(fraction * (endR - startR)表示当前进度下的红色值
startG + (int)(fraction * (endG - startG))表示当前进度下的绿色值
startB + (int)(fraction * (endB - startB))表示当前进度下的蓝色值
然后通过位移和或运算将当前进度下的A,R,G,B组合起来就是当前的颜色值了。
好了,到这里,有关加速器和Evaluator的知识就讲完了,对于ValueAnimator还有一些知识没来得及讲,这篇文章已经很长了,就另开一篇来讲解吧
如果本文有帮到你,记得加关注哦
源码下载地址:
请大家尊重原创者版权,转载请标明出处: 谢谢