Android动画篇(四)—— 属性动画ValueAnimator的高级进阶

前言:成功只属于永不放弃的人。凡诸事有成者都有惊人的毅力做后盾,相信自己,持之以恒。

        上一篇文章给大家介绍了ValueAnimator的基本用法,都是比较简单的用法,这里带大家学习一下有关加速器(Interpolator)、计算器(Evaluator)、ValueAnimator的ofObject用法等相关知识。

一、插值器(Interpolator)

         插值器(也叫加速器)有关的效果我在《Android动画篇(二)—— 代码生成alpha、scale、translate、rotate、set及插值器动画》做了演示。

         什么是插值器?我们知道通过ofInt(0,400)定义了动画区间值为0-400,然后通过添加AnimatorUpdateListener来监听动画实时变化,那么这个变化是怎么样变的呢?是匀速变化还是有快有慢,如果我想先减速后加速,那么该怎么做呢?这就是插值器的作用,插值器就是用来控制动画区间值如果计算出来的。比如LinearInterpolator是匀速计算返回区间点的值,而DecelerateInterpolator则是开始变快,后期变慢,其他都类似。

1、插值器的使用

我们使用弹跳插值器(BounceInterpolator)为例做一个简单的例子,BounceInterpolator就是在动画结束的时候弹跳起来。

        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400);
        valueAnimator.setDuration(1000);
        valueAnimator.setInterpolator(new BounceInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value + 
                mTextView.getHeight());
                Log.e(TAG, "value:" + value);
            }
        });
        valueAnimator.start();

效果图如下:

Android动画篇(四)—— 属性动画ValueAnimator的高级进阶_第1张图片

如果大家看懂了上一篇文章,这里的代码就非常容易理解了。在监听中,我只改变了top和bottom的位置,跟着动画变化的值来改变top和bottom的值,setDuration(1000)设置了动画时长,setInterpolator()设置了插值器,也就是过渡值的变化规则,插值器的意义其实就是相当于物理学中的加速度参数,这也是像加速器的原因。

2、自定义插值器

(1)概述

我们先看看最简单的匀速插值器LinearInterpolator是怎么样的

@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return input;
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
    }
}
abstract public class BaseInterpolator implements Interpolator {
    private @Config int mChangingConfiguration;
    /**
     * @hide
     */
    public @Config int getChangingConfiguration() {
        return mChangingConfiguration;
    }

    /**
     * @hide
     */
    void setChangingConfiguration(@Config int changingConfiguration) {
        mChangingConfiguration = changingConfiguration;
    }
}

        在上面的可以得知LinearInterpolator继承了BaseInterpolator实现了Interpolator接口,而Interpolator 接口直接继承自TimeInterpolator,而且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):

  • float input   是float类型,范围是0-1,表示当前动画的进度,0表示动画刚刚开始,1表示动画结束。0.5表示动画中间位的位                               置,其他的以此类推
  • 返回值        表示当前实际想要显示的进度,取值可以大于1也可以小于0,大于1表示超出动画位置,小于0表示小于开始位置

input参数表示当前动画的进度,匀速增加的。动画进度就是动画时间上的进度,与我们任何设置无关,只与时间有关,随时间的增加,动画进度自然增加,从0到1,我们在setDuration(1000)设置了动画时长的,动画在该时间内完成。

而返回值表示动画进度的具体数值,它的取值范围是对应ofInt()、ofFloat()来指定的,这个返回值就表示当前时间动画的具体进度值。

ValueAnimator valueAnimator = ValueAnimator.ofInt(100, 400);
        valueAnimator.setDuration(1000);
        valueAnimator.setInterpolator(new BounceInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value + mTextView.getHeight());
                Log.e(TAG, "value:" + value);
            }
        });
        valueAnimator.start();

从上面的代码知道,我们设置了AnimatorUpdateListener后,在监听回调函数中设置animation.getAnimatedValue()就可以获得动画具体进度值,那么这个值是怎么来的呢?

计算公式:当前进度值 = 100+(400-100)*显示进度

其中100和400就是我们ofInt(100,400)设置的起点值和终点值,我们要计算位置是,在起始值100的基础上加上实际运动距离(400-100)乘以运动进度。ofInt中的animation.getAnimatedValue()的值就是这么来的。

我们知道了input与getInterpolation()返回值的关系了,来看看LinearInterpolator 是如何重写getInterpolation()方法的?

public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    ········

    public float getInterpolation(float input) {
        return input;
    }

    ········
}

LinearInterpolator 在getInterpolation()函数中,直接把input返回了,把动画的进度作为动画具体的数值进度,也就是动画进度和时间进度一致,比如动画进度为0,那么动画具体数值进度也为0,动画进度为0.5,那么动画具体数值进度也为0.5。

(2)实例

自定义插值器,继承BaseInterpolator就可以了,实现其中的方法getInterpolation(float input),我们将该方法的返回值作了修改,我们将进度翻转过来。然后使用我们的插值器。

public class MyInterpolator extends BaseInterpolator {
    @Override
    public float getInterpolation(float input) {
        return 1 - input;
    }
}

使用自定义的插值器

      ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400);
        valueAnimator.setDuration(1000);
        //设置自定义的插值器
        valueAnimator.setInterpolator(new MyInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value + 
               mTextView.getHeight());
                Log.e(TAG, "value:" + value);
            }
        });
        valueAnimator.start();

效果如下:

Android动画篇(四)—— 属性动画ValueAnimator的高级进阶_第2张图片

我们在将进度数值倒叙返回后,TextView在结束为止(400)返回到开始位置(0)。

到这里,想必大家已经了解getInterpolation(float input)函数中input参数与返回值的关系,在重写插值器时,需要强有力的数学知识做支持,一般都是通过数学公式来计算插值器的变化趋势。你们可以在分析几个其他插值器的写法,可以总结成公式,放在公式插图软件里面,看看对应的数学图在(0,1)之间的走向,这个走势就是插值器在数值变化时的样子。

二、计算器(Evaluator)

1、概述

上面我们提到通过监听器拿到当前动画所对应的具体数值,而不是百分制的进度,那么就有一个地方,根据当前的进度值转化为对应的数值,这个地方就是计算器(Evaluator),Evaluator就是将加速器返回的进度值转化为相对于的数值。其实Evaluator就是一个转换器,他能把小数进度转换成对应的数字值。

上面提到的公式:

当前值 = 100 + (400-100)*显示进度

这个公式就是在Evaluator内计算的,在拿到当前数字进度所对应的值后将其返回。看看下面这幅图:

Android动画篇(四)—— 属性动画ValueAnimator的高级进阶_第3张图片

上面这幅图是从定义动画的数值区间到通过AnimatorUpdateListener获得当前动画所对应具体数值的整个过程,解释一下:

(1)ofInt(0,400)表示指定动画的数值区间,是从0运动到400;

(2)在动画开始之后,通过加速器返回动画进度所对应的数字进度,这个数字进度是百分制的,以小数表示,比如0.2;

(3)Evaluator将加速器返回的进度值转化为相对于的数值;

(4)通过AnimatorUpdateListener监听器使用animation.getAnimatedValue()函数获得Evaluator中返回的数值。

2、各种Evaluator

无论是什么动画它的进度必然是在0-1之间,0表示没开始,1表示结束,任何动画都适用。但是Evaluator不一样,我们知道Evaluator是用来根据加速器Interpolator返回的小数进度转换成当前的数值进度。

那么使用ofInt()来定义动画,动画中的值都是int类型,所对应的Evaluator返回值时,也是返回int类型的数值;如果使用ofFloat()来定义动画,动画中的值都是float类型,所对用的Evaluator返回值时,也是返回float类型的数值。所以每种Evaluator的定义方式必定有对应的Evaluator来使用,他的专用原因在于数值动画类型不一致,在通过Evaluator返回时会报类型强转错误。所以在动画数值类型一致时,Evaluator才能通用,ofInt()对应IntEvaluator,ofFloat()对应FloatEvaluator,在设置Evaluator时通setEvaluator(TypeEvaluator value)来设置:

        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400);
        valueAnimator.setDuration(1000);
        valueAnimator.setEvaluator(new IntEvaluator());
        valueAnimator.setInterpolator(new BounceInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value + 
         mTextView.getHeight());
                Log.e(TAG, "value:" + value);
            }
        });
        valueAnimator.start();

但是之前为什么没有设置Evaluator的时候也能正常使用呢?因为ofInt()和ofFloat()是系统直接提供的函数,在使用时都有默认的Interpolator和Evaluator,没有设置则使用默认的,我们看看IntEvaluator的内部实现:

/**
 * This evaluator can be used to perform type interpolation between 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),
     * where x0 is startValue, x1 is endValue,
     * and t is fraction.
     *
     * @param fraction   The fraction from the starting to the ending values
     * @param startValue The start value; should be of type int or
     *                   Integer
     * @param endValue   The end value; should be of type int or Integer
     * @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));
    }
}

可以看到IntEvaluator里面只有一个函数evaluate(float fraction, Integer startValue, Integer endValue),其中fraction就是加速器中的返回值,表示当前动画的数值进度,百分制小数表示。startValue和endValue分别对应ofInt(int start , int end)的起始值和结束值。比如上面的动画ofInt(100,400)进行到40%时,在Evaluator函数中fraction=0.4,startValue = 100,endValue = 400;

下面我们看看进度小数值计算出来的具体数值:

return (int)(startInt + fraction * (endValue - startInt));

这不正是对应着我上面提到的公式吗?

当前值 = 100 + (400-100)*显示进度

计算原理我们在上面已经讲过,根据进度来计算具体数值。我们来自定义一个Evaluator

3、自定义Evaluator

(1)简单实现MyEvaluator

我们定义一个Evaluator,首先实现TypeEvaluator<>,根据数值类型传入泛型类型:

public class MyEvaluator implements TypeEvaluator {
    @Override
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        return null;
    }
}

我们来实现简单的evaluate函数:

return (int) (200 + startValue + fraction * (endValue - startValue));

根据IntEvaluator的基础上做了修改,让返回值增加了200,所以开始定义的区间是ofInt(0,400)但是实际返回的区间是ofInt(200,600),看看怎么使用:

        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400);
        valueAnimator.setDuration(1000);
        valueAnimator.setEvaluator(new MyEvaluator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value + 
            mTextView.getHeight());
                Log.e(TAG, "value:" + value);
            }
        });
        valueAnimator.start();

效果图如下:

Android动画篇(四)—— 属性动画ValueAnimator的高级进阶_第4张图片

从效果图可看出TextView的动画位置都向下移动了200。

在加速器Interpolator中,我们通过自定义加速器返回的数值进度来返回数值的位置,比如上面我们实现的倒叙动画

在计算器Evaluator中,通过改变进度值所对应的具体数值来改变数值位置。

结论:我们可以通过重写加速器Interpolator来改变数值进度来改变数值位置,也可以通多改变计算器Evaluator中进度所对应的数值来改变数值位置。

(2)实现倒叙输出

我们来定义一个ReverseEvaluator来实现倒叙:

public class ReverseEvaluator implements TypeEvaluator {
    @Override
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        return (int) (endValue - fraction * (endValue - startValue));
    }
}

其中fraction * (endValue - startValue)表示实际运动的距离,endValue - fraction * (endValue - startValue)表示离终点的距离有多少,实现了从终点出发,最终到达起点。我们使用试一试:

 valueAnimator.setEvaluator(new ReverseEvaluator());

效果如下:

Android动画篇(四)—— 属性动画ValueAnimator的高级进阶_第5张图片

4、关于ArgbEvaluator

ArgbEvaluator使用来做颜色值转换的,我们先来使用一下,最后再讲源码:

在源码我们可以看出返回值时Int类型,可以使用ofInt()来初始化动画的取值范围,颜色值包括A、R、G、B四个值,每个值都是8位,用16进制来表示颜色值。

        ValueAnimator valueAnimator = ValueAnimator.ofInt(0xff000000, 0xffffffff);
        valueAnimator.setDuration(2000);
        valueAnimator.setEvaluator(new ArgbEvaluator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                mTextView.setBackgroundColor(value);
                Log.e(TAG, "value:" + value);
            }
        });
        valueAnimator.start();

我们将动画的取值范围定义为ofInt(0xff000000, 0xffffffff),即从黑色变为白色,在AnimatorUpdateListener中将回传回来的颜色值设置在TextView的背景颜色中。

Android动画篇(四)—— 属性动画ValueAnimator的高级进阶_第6张图片

点击进去看一看ArgbEvaluator的源码:

  /**
     * 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;
        float startA = ((startInt >> 24) & 0xff) / 255.0f;
        float startR = ((startInt >> 16) & 0xff) / 255.0f;
        float startG = ((startInt >>  8) & 0xff) / 255.0f;
        float startB = ( startInt        & 0xff) / 255.0f;

        int endInt = (Integer) endValue;
        float endA = ((endInt >> 24) & 0xff) / 255.0f;
        float endR = ((endInt >> 16) & 0xff) / 255.0f;
        float endG = ((endInt >>  8) & 0xff) / 255.0f;
        float endB = ( endInt        & 0xff) / 255.0f;

        // convert from sRGB to linear
        startR = (float) Math.pow(startR, 2.2);
        startG = (float) Math.pow(startG, 2.2);
        startB = (float) Math.pow(startB, 2.2);

        endR = (float) Math.pow(endR, 2.2);
        endG = (float) Math.pow(endG, 2.2);
        endB = (float) Math.pow(endB, 2.2);

        // compute the interpolated color in linear space
        float a = startA + fraction * (endA - startA);
        float r = startR + fraction * (endR - startR);
        float g = startG + fraction * (endG - startG);
        float b = startB + fraction * (endB - startB);

        // convert back to sRGB in the [0..255] range
        a = a * 255.0f;
        r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
        g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
        b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;

        return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
    }

这段代码主要分为三部分:

(1)根据startValue求出A、R、G、B中各个色彩的初始值;

(2)根据endValue求出A、R、G、B的各个结束值;

(3)根据当前动画百分比进度求出对应的值。

第一部分:根据startValue求出A、R、G、B中各个色彩的初始值

        int startInt = (Integer) startValue;
        float startA = ((startInt >> 24) & 0xff) / 255.0f;
        float startR = ((startInt >> 16) & 0xff) / 255.0f;
        float startG = ((startInt >>  8) & 0xff) / 255.0f;
        float startB = ( startInt        & 0xff) / 255.0f;

这段代码是根据位移和与运算计算出颜色值中A、R、G、B各部分对应的值,颜色值与A、R、G、B值关系如下:

Android动画篇(四)—— 属性动画ValueAnimator的高级进阶_第7张图片

Android中的颜色值遵循RGB/ARGB标准,使用时通常以"#"为为开头的8位16进制表示。其中ARGB依次代表透明度(alpha)、红色(red)、绿色(green)、蓝色(blue);取值范围为0~255,即十六进制的0x00~0xff表示。A从0x00到0xff表示完全透明到完全不透明,RGB从0x00到0xff表示颜色从浅到深。

所以我们的初始值是0xff000000,求出来的startA = 0xff,startR = 0x00,startG = 0x00,startB = 0x00;关于位移和与运算我就不讲了。

第二部分:和第一部分原理一致,根据endValue求出A、R、G、B的各个结束值的颜色值:

        int endInt = (Integer) endValue;
        float endA = ((endInt >> 24) & 0xff) / 255.0f;
        float endR = ((endInt >> 16) & 0xff) / 255.0f;
        float endG = ((endInt >>  8) & 0xff) / 255.0f;
        float endB = ( endInt        & 0xff) / 255.0f;

我们的结束值是:0xffffffff,求出来的endA = oxff,endR = oxff,endG = oxff,endB = oxff;

最后一部分:根据当前动画百分比进度求出对应的值

        float a = startA + fraction * (endA - startA);
        float r = startR + fraction * (endR - startR);
        float g = startG + fraction * (endG - startG);
        float b = startB + fraction * (endB - startB);

对于这个计算都很容易理解,其实和Evalustor的计算公式一样,根据起始值和结束值计算出该进度下的具体值。最后通过位移和与运算将当下的ARGB各个值组合成当前的颜色值。

三、ofObject

1、ofObject的概述

前面讲了ValueAnimator使用ofInt()和ofFloat()来定义动画,但是ofInt()和ofFloat()分别只能传入Int和Float类型的参数,如果我们需要其他操作类型的怎么办?ValueAnimator还有一个ofObject()类型的参数,可以传入任何类型的变量:

public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)

它有两个参数,一个是自定义的Evaluator;一个是可变长参数,object类型

大家可能有疑问,为什么要自定义Evaluator,因为Evaluator是根据动画当前的进度来计算当前对应的值的,如果可变参数类型是object,那么具体的类型就是未知的,所以Evaluator无法根据类型来得出结果,进度值的转换过程也必须由我们来做,我们来看看ofObject()的使用效果:

Android动画篇(四)—— 属性动画ValueAnimator的高级进阶_第8张图片

从效果图可以看出,字母A变化到Z,并且变化速度越来越快。

 ValueAnimator valueAnimator = ValueAnimator.ofObject(new CharEvaluator(), new Character('A'), new Character('Z'));
        valueAnimator.setDuration(6000);
        valueAnimator.setInterpolator(new AccelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                char value = (char) animation.getAnimatedValue();
                mTextView.setText(String.valueOf(value));
                Log.e(TAG, "value:" + value);
            }
        });
        valueAnimator.start();

这里注意三点:

(1)构造器:

ValueAnimator.ofObject(new CharEvaluator(), new Character('A'), new Character('Z'));

我们定义了一个CharEvaluator(后面会讲),初始化动画时,传入Character对象,起始是A,结束是Z,利用动画自动变化字母A到字母Z,具体的实现就是CharEvaluator的事情了;

(2)AnimatorUpdateListener监听

      char value = (char) animation.getAnimatedValue();
      mTextView.setText(String.valueOf(value));

通过animation.getAnimatedValue()得到当前动画字符,在动画过程中通过Evaluator返回值的类型必然和构造时的类型时一样的,由于传入的是Character,所以得到的也是Character,转为字符串后设置到TextView;

(3)插值器

valueAnimator.setInterpolator(new AccelerateInterpolator());

这里我们使用的加速插值器,随着动画的进行,速度会越来越快。

好,最关键的地方到了,CharEvaluator是怎么实现的呢?ASCII码表中的数值与字符转换的方法,每一个字符都有一个跟它相对应的数值,字母A到字母Z所对应的数值区间为65-90,在程序中我们可以通过强转来改变类型。

比如:数字转字符

char temp = (char)65;//得到的temp的值是大写字母A

字符转数值

    char temp = 'A';
    int num = (int)temp;//这里的到的数值就是ASCII码表中A对应的值65

CharEvaluator的实现:

public class CharEvaluator implements TypeEvaluator {
    @Override
    public Character evaluate(float fraction, Character startValue, Character endValue) {
        int startInt = (int) startValue;
        int endInt = (int) endValue;
        int value = (int) (startInt + fraction * (endInt - startInt));
        return (char) value;
    }
}

在这里,我们就是利用A-Z字母在ASCII字码表中数值递增的原理,先求出对应字符的数值,再将数值转化为具体字符。

2、ofObject的自定义对象

上面我们初度使用了ofObject(),ofObject()能初始化任何对像,我们来自定义一个对象,然后利用ofObject()来构造这个对象的动画。

Android动画篇(四)—— 属性动画ValueAnimator的高级进阶_第9张图片

在这里,我们自定义一个view,在view上画一个圆,有动画效果,插值器使用的是回弹插值器(BounceInterpolator)

(1)定义类Point

public class Point {
    private int radius;

    public Point(int radius) {
        this.radius = radius;
    }

    public int getRadius() {
        return radius;
    }

    public void setRadius(int radius) {
        this.radius = radius;
    }
}

point类很简单,只有一个成员变量radius

(2)自定义MyPointView

public class MyPointView extends View {
    private Point mPoint;

    public MyPointView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mPoint != null) {
            Paint paint = new Paint();
            paint.setColor(Color.RED);
            paint.setStyle(Paint.Style.FILL);
            paint.setAntiAlias(true);
            canvas.drawCircle(500f, 600f, mPoint.getRadius(), paint);
        }
    }

    public void doAnimator() {
        ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), new Point(20), new Point(300));
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mPoint = (Point) animation.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.setDuration(1000);
        valueAnimator.setInterpolator(new BounceInterpolator());
        valueAnimator.start();
    }
}

我们先来理一理这里的代码:先看看外部调用的动画函数

public void doAnimator() {
        ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), new Point(20), new Point(300));
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mPoint = (Point) animation.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.setDuration(1000);
        valueAnimator.setInterpolator(new BounceInterpolator());
        valueAnimator.start();
    }

ofObject()的构造方法:

ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), new Point(20), new Point(300));

这里可以看到,在构造动画时,动画所对应的值是Point类型,那么在Evaluator中的返回值类型也是Point,有关PointEvaluator下面再讲,下面看看监听中的代码:

 valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mPoint = (Point) animation.getAnimatedValue();
                invalidate();
            }
        });

在监听过程中,通过animation.getAnimatedValue()得到的mPoint实例,保存在成员变量中,然后 invalidate()强制刷新View。在强制刷新后,会走onDraw()方法:

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mPoint != null) {
            Paint paint = new Paint();
            paint.setColor(Color.RED);
            paint.setStyle(Paint.Style.FILL);
            paint.setAntiAlias(true);
            canvas.drawCircle(500f, 600f, mPoint.getRadius(), paint);
        }
    }

这里设置画笔的属性,在(500,600)的位置画出圆形。在构造ofObject()函数中,初始值和中间值都是Point类型,PointEvaluator的返回值类型也是Point:

public class PointEvaluator implements TypeEvaluator {
    @Override
    public Point evaluate(float fraction, Point startPoint, Point endPoint) {
        int startInt = startPoint.getRadius();
        int endInt = endPoint.getRadius();
        int value = (int) (startInt + fraction * (endInt - startInt));
        return new Point(value);
    }
}

这里根据初始半径和最终半径求出当前动画所对应的半径值,然后构建一个Point对象。

(3)使用MyPointView

在布局中添加控件:





    

    

    

    


调用控件的属性:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = MainActivity.class.getSimpleName();
    private TextView mTextView;
    private MyPointView myPointView;
    private ValueAnimator mValueAnimator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myPointView = findViewById(R.id.myPointView);
        mTextView = findViewById(R.id.tv);
        findViewById(R.id.tv_start_anim).setOnClickListener(this);
        findViewById(R.id.tv_cancel).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.tv_start_anim:
                setMyPointView();
                break;
            case R.id.tv_cancel:
                if (mValueAnimator != null) {
                    //退出动画
                    mValueAnimator.cancel();
                }
                break;
            default:
                break;
        }
    }

    /**
     * ofObject的使用2
     */
    private void  setMyPointView() {
        myPointView.doAnimator();
    }
}

点击start anim按钮时调用myPointView.doAnimator()方法开始动画。

至此,本文结束!篇幅比较长。

源码下载地址:https://github.com/FollowExcellence/AndroidAnimation

请大家尊重原创者版权,转载请标明出处: https://blog.csdn.net/m0_37796683/article/details/90483462 谢谢!

动画系列文章:

1、 Android动画篇(一)—— alpha、scale、translate、rotate、set的xml属性及用法

  • 补间动画的XML用法以及属性详解

2、Android动画篇(二)—— 代码实现alpha、scale、translate、rotate、set及插值器动画

  • 代码动态实现补间动画以及属性详解

3、 Android动画篇(三)—— 属性动画ValueAnimator的使用

  • ValueAnimator的基本使用

4、 Android动画篇(四)—— 属性动画ValueAnimator的高级进阶

  • 插值器(Interpolator)、计算器(Evaluator)、ValueAnimator的ofObject用法等相关知识

5、 Android动画篇(五)—— 属性动画ObjectAnimator基本使用

  • ObjectAnomator的基本使用以及属性详解

6、 Android动画篇(六)—— 组合动画AnimatorSet和PropertyValuesHolder的使用

  • AnimatorSet动画集合和PropertyValuesHolder的使用

以上几篇动画文章是一定要掌握的,写的不好请多多指出!

你可能感兴趣的:(Android动画系列)