Android graphic details attribute animation



The main classes in property animation are shown in the following figure:

write picture description here

Animator

The main classes of attribute animation are all under the android.animation namespace. Animator is the base class of attribute animation. It is an abstract class that defines many important methods, as follows:

  • setDuration(long duration) The total duration of the animation can be set in milliseconds through the setDuration method. 

  • start() The animation can be started through the start method, and the animation may not run immediately after it is started. If the animation delay time is previously set by calling the setStartDelay method, the animation will run after the delay time has elapsed; if the animation delay time has not been set, the animation will run immediately after calling the start() method. After the start() method is called, the isStarted() method of the animation will return true; after the animation is actually running, the isRunning() method of the animation will return true, at this time the animation will call TimeInterpolator to start working to calculate the property value at a certain time. The thread where the animation's start() method is called must be bound to a Looper object. If there is no binding, an error will be reported. Of course, the UI thread (that is, the main thread) has already bound a Looper object by default, so we don't need to worry about this problem in the main thread. If we want to use property animation on a View, then we should make sure that we call the animation's start() method on the UI thread. After the start() method runs, it will trigger the execution of the onAnimationStart method of the animation listener AnimatorListener. 

  • setStartDelay(long startDelay) can set the delayed running time of the animation by calling the setStartDelay method. For example, calling setStartDelay(1000) means that the animation will not actually run until 1 second after the start() method is executed. In this case, after calling start( ) method, the isStarted() method returns true, indicating that the animation has been started, but within 1s after the start() method is called, the isRunning() method returns false, indicating that the animation has not really run, such as after the start() method is called. At 0.5 seconds, because the animation is still in the delay phase, isRunning() returns false; after the start() method is executed for 1 second, the isStarted() method still returns true, and the isRunning() method also returns true, indicating that the animation has really started. run. By calling the getStartDelay() method, the animation delay start time we set can be returned. The default value is 0. 

  • setInterpolator(TimeInterpolator value) We can change the time interpolator used by the animation by calling the setInterpolator method. Since the view animation also needs to use the time interpolator, we can use a series of interpolators under the android.view.animation namespace to make it Works with property animation. The time interpolator we set can be obtained through the getInterpolator method of animation. 

  • setTarget(Object target) can set the object to be operated by calling the setTarget method of the animation, so that a property value of the object can be updated. In fact, this method has little effect on the ValueAnimator, because the ValueAnimator does not directly deal with an object. The setTarget method has a great effect on ObjectAnimator, because ObjectAnimator needs to bind an object to be manipulated, which will be described in detail below. 

  • pause() 
    Android中API Level 19在Animator中加入了pause()方法,该方法可以暂停动画的执行。调用pause()方法的线程必须与调用start()方法的线程是同一个线程。如果动画还没有执行start()或者动画已经结束了,那么调用pause()方法没有任何影响,直接被忽略。当执行了pause()方法后,动画的isPaused()方法会返回true。pause()方法运行后会触发动画监听器AnimatorPauseListener的onAnimationPause方法的执行。

  • resume() 
    如果动画通过调用pause()方法暂停了,那么之后可以通过调用resume()方法让动画从上次暂停的地方继续运行。resume()方法也是从API Level 19引入的,并且调用resume()方法的线程必须与调用start()方法的线程是同一个线程。如果动画没有处于暂停状态(即isPaused()返回false),那么调用resume()方法会被忽略。resume()方法运行后会触发动画监听器AnimatorPauseListener的onAnimationResume方法的执行。

  • end 
    end()方法执行后,动画会结束运行,直接从当前状态跳转到最终的完成状态,并将属性值分配成动画的终止值,并触发动画监听器AnimatorListener的onAnimationEnd方法的执行。

  • cancel() 
    cancel()方法执行后,动画也会结束运行,但是与调用end方法不同的是,其不会将属性值分配给动画的终止值。比如一个动画在400ms内将对象的x属性从0渐变为40,当运行到第200ms时调用了cancel()方法,那么属性x的最终值是20,而不是40,这是与调用end()方法不同的,如果在第200ms调用了end()方法,那么属性x的最终值是40。调用cancel()方法后,会先触发AnimatorListener的onAnimationCancel方法的执行,然后触发onAnimationEnd方法的执行。

  • clone() 
    Animator实现了java.lang.Cloneable接口。Animator的clone()方法会对动画进行拷贝,但是该方法默认实现的只是浅拷贝,子类可以重写该方法以实现深拷贝。

  • addListener (Animator.AnimatorListener listener) 
    可以通过addListener方法向Animator添加动画监听器,该方法接收的是AnimatorListener接口类型的参数,其具有四个方法:onAnimationStart、onAnimationCancel、onAnimationEnd、onAnimationRepeat。我们上面已经介绍了前三个方法,onAnimationRepeat方法会在动画在重复播放的时候被回调。Android中的AnimatorListenerAdapter类是个抽象类,其实现了AnimatorListener接口,并为所有方法提供了一个空实现。

  • addPauseListener (Animator.AnimatorPauseListener listener) 
    可以通过addPauseListener方法可以向Animator添加动画暂停相关的监听器,该方法接收的是AnimatorPauseListener接口类型的参数,具有两个方法:onAnimationPause和onAnimationResume,上面已经提到过,在此不再赘述。AnimatorListenerAdapter同样也实现了AnimatorPauseListener接口,并为所有方法提供了一个空实现。

ValueAnimator

ValueAnimator继承自抽象类Animator。要让属性动画渐变式地更改对象中某个属性的值,可分两步操作:第一步,动画需要计算出某一时刻属性值应该是多少;第二步,需要将计算出的属性值赋值给动画的属性。ValueAnimator只实现了第一步,也就是说ValueAnimator只负责以动画的形式不断计算不同时刻的属性值,但需要我们开发者自己写代码将计算出的值通过对象的setXXX等方法更新对象的属性值。

ValueAnimator中有两个比较重要的属性,一个是TimeInterpolator类型的属性,另一个是TypeEvaluator类型的属性。TimeInterpolator指的就是时间插值器,在上面我们已经介绍过,在此不再赘述。TypeEvaluator是什么呢?TypeEvaluator表示的是ValueAnimator对哪种类型的值进行动画处理。ValueAnimator提供了四个静态方法ofFloat()、ofInt()、ofArgb()和ofObject(),通过这四个方法可以对不同种类型的值进行动画处理,这四个方法对应了四种TypeEvaluator,下面会详细说明。

  • public static ValueAnimator ofFloat (float… values) 
    ofFloat方法接收一系列的float类型的值,其内部使用了FloatEvaluator。通过该方法ValueAnimator可以对float值进行动画渐变,其使用方法如下所示:

        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 500f);
    
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float deltaY = (float)animation.getAnimatedValue();
                textView.setTranslationY(deltaY);
            }
        });
    
        //默认duration是300毫秒
        valueAnimator.setDuration(3000);
        valueAnimator.start();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    其效果如下所示: 
    write picture description here

    我们通过构造函数指定了动画的起始值为0,终止值为500,动画的默认持续时间是300毫秒,我们通过setDuration()方法设置为3000毫秒。该动画会在3秒内,将值从0到500动画渐变。ValueAnimator提供了一个addUpdateListener方法,可以通过该方法向其添加AnimatorUpdateListener类型的监听器。AnimatorUpdateListener有一个onAnimationUpdate方法,ValueAnimator会每隔一定时间(默认间隔10ms)计算属性的值,每当计算的时候就会回调onAnimationUpdate方法。在该方法中,我们通过调用ValueAnimator的getAnimatedValue()方法获取到当前动画计算出的属性值,然后我们将该值传入textView的setTranslationY()方法中,从而更新了textView的位置,这样就通过ValueAnimator以动画的形式移动textView。

  • public static ValueAnimator ofInt (int… values) 
    ofInt方法与ofFloat方法很类似,只不过ofInt方法接收int类型的值,ofInt方法内部使用了IntEvaluator,其具体使用可参考上面ofFloat的使用代码,在此不再赘述。

  • public static ValueAnimator ofArgb (int… values) 
    从API Level 21开始,ValueAnimator中加入了ofArgb方法,该方法接收一些列代表了颜色的int值,其内部使用了ArgbEvaluator,可以用该方法实现将一个颜色动画渐变到另一个颜色,我们从中可以不断获取中间动画产生的颜色值。你可能纳闷,既然传入的还是int值,那直接用ofInt方法不就行了吗,干嘛还要新增一个ofArgb方法呢?实际上用ofInt方法是不能完成颜色动画渐变的。我们知道一个int值包含四个字节,在Android中第一个字节代表Alpha分量,第二个字节代表Red分量,第三个字节代表Green分量,第四个字节代表Blue分量,且我们常用16进制表示颜色,这样看起来更明显易懂一些,比如int值0xffff0000表示的红色,0xff00ff00表示的是绿色,最前面的ff表示的是Alpha。ofArgb方法会通过ArgbEvaluator将颜色拆分成四个分量,然后分别对各个分量进行动画计算,然后将四个计算完的分量再重新组合成一个表示颜色的int值,这就是ofArgb方法的工作原理。使用方法如下所示:

        //ValueAnimator.ofArgb()方法是在API Level 21中才加入的
        if(Build.VERSION.SDK_INT >= 21){
            //起始颜色为红色
            int startColor = 0xffff0000;
            //终止颜色为绿色
            int endColor = 0xff00ff00;
            ValueAnimator valueAnimator = ValueAnimator.ofArgb(startColor, endColor);
            valueAnimator.setDuration(3000);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int color = (int)animation.getAnimatedValue();
                    textView.setBackgroundColor(color);
                }
            });
            valueAnimator.start();
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    效果如下所示: 
    write picture description here

    我们将TextView的颜色通过动画从红色渐变到绿色。

  • public static ValueAnimator ofObject (TypeEvaluator evaluator, Object… values) 
    由于我们要进行动画处理的值是各种各样的,可能不是float、int或颜色值,那我们怎么使用属性动画呢?为此,ValueAnimator提供了一个ofObject方法,该方法接收一个TypeEvaluator类型的参数,我们需要实现该接口TypeEvaluator的evaluate方法,只要我们实现了TypeEvaluator接口,我们就能通过ofObject方法处理任意类型的数据。我们之前提到ofArgb方法是从API Level 21才引入的,如果我们想在之前的这之前的版本中使用ofArgb的功能,怎么办呢?我们可以扩展TypeEvaluator,从而通过ofObject方法实现ofArgb方法的逻辑,如下所示:

        //起始颜色为红色
        int startColor = 0xffff0000;
        //终止颜色为绿色
        int endColor = 0xff00ff00;
        ValueAnimator valueAnimator = ValueAnimator.ofObject(new TypeEvaluator() {
            @Override
            public Object evaluate(float fraction, Object startValue, Object endValue) {
                //从初始的int类型的颜色值中解析出Alpha、Red、Green、Blue四个分量
                int startInt = (Integer) startValue;
                int startA = (startInt >> 24) & 0xff;
                int startR = (startInt >> 16) & 0xff;
                int startG = (startInt >> 8) & 0xff;
                int startB = startInt & 0xff;
    
                //从终止的int类型的颜色值中解析出Alpha、Red、Green、Blue四个分量
                int endInt = (Integer) endValue;
                int endA = (endInt >> 24) & 0xff;
                int endR = (endInt >> 16) & 0xff;
                int endG = (endInt >> 8) & 0xff;
                int endB = endInt & 0xff;
    
                //分别对Alpha、Red、Green、Blue四个分量进行计算,
                //最终合成一个完整的int型的颜色值
                return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
                        (int)((startR + (int)(fraction * (endR - startR))) << 16) |
                        (int)((startG + (int)(fraction * (endG - startG))) << 8) |
                        (int)((startB + (int)(fraction * (endB - startB))));
            }
        }, startColor, endColor);
        valueAnimator.setDuration(3000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int color = (int)animation.getAnimatedValue();
                textView.setBackgroundColor(color);
            }
        });
        valueAnimator.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

    以上代码实现的效果与ofArgb实现的效果是一样的,都是将TextView从红色渐变到绿色,就不再附图了,但是我们可以在API Level 11及以后的版本中都可以使用以上ofObject的代码,通用性更强。


ObjectAnimator

ObjectAnimator继承自ValueAnimator。我们之前提到,要让属性动画渐变式地更改对象中某个属性的值,可分两步操作:第一步,动画需要计算出某一时刻属性值应该是多少;第二步,需要将计算出的属性值赋值给动画的属性。ValueAnimator只实现了第一步,也就是说ValueAnimator只负责以动画的形式不断计算不同时刻的属性值,但需要我们开发者自己写代码在动画监听器AnimatorUpdateListener的onAnimationUpdate方法中将计算出的值通过对象的setXXX等方法更新对象的属性值。ObjectAnimator比ValueAnimator更进一步,其会自动调用对象的setXXX方法更新对象中的属性值。

ObjectAnimator重载了ofFloat()、ofInt()、ofArgb()和ofObject()等静态方法,我们下面依次说明。

  • ofFloat(Object target, String propertyName, float… values) 
    使用方法如下所示:

        float value1 = 0f;
        float value2 = 500f;
        final ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(textView, "translationY", value1, value2);
        objectAnimator.setDuration(3000);
        objectAnimator.start();
    • 1
    • 2
    • 3
    • 4
    • 5

    以上代码实现的效果与通过ValueAnimator的ofFloat方法实现的效果相同,此处不再附图,但是可以看出使用ObjectAnimator代码更简洁。在构造函数中,我们将textView作为target传递给ObjectAnimator,然后指定textView要变化的属性是translationY,最后指定渐变范围是从0到500。当动画开始时,ObjectAnimator就会不断调用textView的setTranslationY方法以更新其值。我们此处演示的是ObjectAnimator与View一起工作,其实ObjectAnimator可以与任意的Object对象工作。如果要更新某个对象中名为propery的属性,那么该Object对象必须具有一个setProperty的setter方法可以让ObjectAnimator调用。在ofFloat方法最后如果只填入了一个float值,那么ObjectAnimator需要调用对象的getXXX方法获取对象初始的属性值,然后从该初始的属性值渐变到终止值。

  • ofInt(Object target, String propertyName, int… values) 
    参见ofFloat的使用方法。

  • ofArgb(Object target, String propertyName, int… values) 
    使用代码如下所示:

        //ObjectAnimator.ofArgb()方法是在API Level 21中才加入的
        if(Build.VERSION.SDK_INT >= 21){
            int startColor = 0xffff0000;
            int endColor = 0xff00ff00;
            ObjectAnimator objectAnimator = ObjectAnimator.ofArgb(textView, "backgroundColor", startColor, endColor);
            objectAnimator.setDuration(3000);
            objectAnimator.start();
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    效果图参见ValueAnimator中对应的图片。

  • ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object… values) 
    使用代码如下所示:

        int startColor = 0xffff0000;
        int endColor = 0xff00ff00;
        ObjectAnimator objectAnimator = ObjectAnimator.ofObject(textView, "backgroundColor", new TypeEvaluator() {
            @Override
            public Object evaluate(float fraction, Object startValue, Object endValue) {
                int startInt = (Integer) startValue;
                int startA = (startInt >> 24) & 0xff;
                int startR = (startInt >> 16) & 0xff;
                int startG = (startInt >> 8) & 0xff;
                int startB = startInt & 0xff;
    
                int endInt = (Integer) endValue;
                int endA = (endInt >> 24) & 0xff;
                int endR = (endInt >> 16) & 0xff;
                int endG = (endInt >> 8) & 0xff;
                int endB = endInt & 0xff;
    
                return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
                        (int)((startR + (int)(fraction * (endR - startR))) << 16) |
                        (int)((startG + (int)(fraction * (endG - startG))) << 8) |
                        (int)((startB + (int)(fraction * (endB - startB))));
            }
        }, startColor, endColor);
        objectAnimator.setDuration(3000);
        objectAnimator.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

    效果图参见ValueAnimator中对应的图片。


AnimatorSet

AnimatorSet inherits from Animator. AnimatorSet represents a collection of animations. We can group multiple animations together through AnimatorSet and let them execute serially or in parallel to create complex animation effects.

We want the TextView to rotate first, then translate, and finally expand and contract. We can achieve this effect through AnimatorSet. The code is as follows:

```
    //anim1实现TextView的旋转动画
    Animator anim1 = ObjectAnimator.ofFloat(textView, "rotation", 0f, 360f);
    anim1.setDuration(2000);
    //anim2和anim3TextView的平移动画
    Animator anim2 = ObjectAnimator.ofFloat(textView, "translationX", 0f, 300f);
    anim2.setDuration(3000);
    Animator anim3 = ObjectAnimator.ofFloat(textView, "translationY", 0f, 400f);
    anim3.setDuration(3000);
    //anim4实现TextView的伸缩动画
    Animator anim4 = ObjectAnimator.ofFloat(textView, "scaleX", 1f, 0.5f);
    anim4.setDuration(2000);


    //第一种方式
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.playSequentially(anim1, anim2, anim4);
    animatorSet.playTogether(anim2, anim3);
    animatorSet.start();

    //第二种方式
    /*AnimatorSet anim23 = new AnimatorSet();
    anim23.playTogether(anim2, anim3);
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.playSequentially(anim1, anim23, anim4);
    animatorSet.start();*/

    //第三种方式
    /*AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.play(anim1).before(anim2);
    animatorSet.play(anim2).with(anim3);
    animatorSet.play(anim4).after(anim2);
    animatorSet.start();*/
```

The effect is as follows: 
write picture description here


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324895250&siteId=291194637