Android动画(三)之属性动画(Property Animation)

学习资源来自:

http://blog.csdn.net/guolin_blog/article/details/43536355

http://blog.csdn.net/u011692041/article/details/51471743

https://sanwen8.cn/p/197zRno.html

属性动画是 API 11 引进的,之前有说补间动画只是改变了View的视觉效果,但是它的自身属性并没有改变,动画重新播放的时候还是从原来的地方开始播放。而属性动画顾名思义,就是通过改变View(其实不单可以是View)的属性来达到动画的效果。

简单来说,属性动画允许 Android 中任意对象“动”起来,设定一个持续时长,属性动画在这个时长内改变这个对象某些属性的值,比如一个View的位置,大小等。

属性动画很强大,我刚看也不可能全弄明白,来了解一下我觉得比较关键的几个类和接口的基本用法:


1 ValueAnimator

它大概是属性动画的核心类了,ValueAnimator可以帮我们完成在给定事件内,从初始值平滑地过渡到结束值的效果。

它有多个工厂方法来获取实例,常用的是 ofFloat() 、 ofInt() 方法,还有一个 ofObject() 方法,是对任意对象进行动画操作的,但是使用起来就不是那么简单了,所以我就先不深究。

于是就可以这样开始属性动画:

                //得到ValueAnimator实例
                ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0f, 1f);
                //动画时长
                mValueAnimator.setDuration(1000);
                //动画重复次数
                mValueAnimator.setRepeatCount(3);
                //动画重复模式,有两个可选参数:ValueAnimator.RESTART 重新开始;ValueAnimator.REVERSE 逆向重复
                mValueAnimator.setRepeatMode(ValueAnimator.REVERSE);
                //开始动画
                mValueAnimator.start();
这段代码的意思就是在1000毫秒内,将一个 float 型的值从 0 过渡到 1 ,重复 3 次,重复模式为逆向重复。但是我们只是开启了这个动画,并不知道它是否真正的执行了,这时候就需要添加监听器了。

2 AnimatorListener、AnimatorPauseListener、AnimatorUpdateListener

ValueAnimator继承自Animator,所以它也可以添加父类的两个监听器AnimatorListener,AnimatorPauseListener,它自己也有一个监听器AnimatorUpdateListener。

AnimatorListener:

    public interface AnimatorListener {
        void onAnimationStart(Animator var1);

        void onAnimationEnd(Animator var1);

        void onAnimationCancel(Animator var1);

        void onAnimationRepeat(Animator var1);
    }
监听动画过程,有四个回调方法,看方法名就能看出来,分别在动画开始、动画结束、取消动画、重复动画时回调,所以我们可以在对应方法内实现自己的逻辑。
AnimatorPauseListener:
    public interface AnimatorPauseListener {
        void onAnimationPause(Animator var1);

        void onAnimationResume(Animator var1);
    }

监听动画的暂停,分别在暂停动画和恢复动画时回调,需要注意这个监听器时 API 19 才引入的。
AnimatorUpdateListener:

    public interface AnimatorUpdateListener {
        void onAnimationUpdate(ValueAnimator var1);
    }

监听动画的每一帧,当动画有变化时即回调,所以我们可以添加这个监听器来看看动画是否真正执行了。

               mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                        //valueAnimator.getAnimatedValue()的返回值是Object型的
                        //它的具体类型跟创建ValueAnimator实例的方法有关,如果是onFloat()创建的,则返回float型
                        //如果是onInt()创建的,则返回int型,如果是onObject()创建的则返回Object
                        Log.i("AnimationDemo", "value:" + valueAnimator.getAnimatedValue());
                    }
                });


我这里只截取了部分打印,可以看到属性动画的值确实是时刻在改变的。


3 ObjectAnimator

它是 ValueAnimator 的子类,用法跟 ValueAnimator 类似。一般来说我们用它比用 ValueAnimator 还要多一些,因为 ValueAnimator 提供的是一个值的平滑过渡过程,而 ObjectAnimator 可以直接对任意对象的任意属性进行动画操作,比如补间动画中的旋转属性。
只需要这样写代码,就可以为指定控件添加旋转动画了:

                ObjectAnimator mObjectAnimator = ObjectAnimator.ofFloat(ivBasketball, "rotation", 0, 180);
                mObjectAnimator.setDuration(1000);
                mObjectAnimator.start();
效果如下:


重复点击发现,诶,不是说属性动画会改变 View 的自身属性吗,但是为什么还是每次从头开始旋转的 180° 啊?不急,先来看看 ObjectAnimator 获取实例的方法的参数都是什么意思。

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {}
target 自然指的是需要添加动画的目标对象。
propertyName 则是要改变的属性。
float... values:这是一个可变参数,长度为 1 时,传入值为结束值,开始值则为目标对象指定属性的当前值;当长度大于 1 时,传入的第一个值为开始值,后面的值则为依次变化的值。
所以并不是没有改变这个对象的属性,而是我们指定了每次动画的开始值都为 0 ,稍微改一下,把开始值改成当前位置:

                ObjectAnimator mObjectAnimator = ObjectAnimator.ofFloat(ivBasketball, "rotation", ivBasketball.getRotation(), ivBasketball.getRotation() + 180);
                mObjectAnimator.setDuration(1000);
                mObjectAnimator.start();
效果就和预期的一样了:


propertyName 不只是 alpha 、 rotationX 、 ranslationY 、 scaleX 这些 View 的属性,而是任意属性,因为属性动画本来就不是专门为 View 设计的,所以它可以指定任意属性,然后再传入参数来改变指定对象的指定属性。
前面说到 float...values 是可变参数,长度大于 1 时,值依次变化,所以我们可以添加变化次数更多的动画:

                ObjectAnimator mObjectAnimator = ObjectAnimator.ofFloat(ivBasketball, "translationY", 0, 500, 0);
                mObjectAnimator.setDuration(2000);
                mObjectAnimator.start();
效果如下:


是不是很像在运球,哈哈,看来这个图片选得还是不错的。


4 AnimatorSet

动画集合,没错,它就是用来存放动画的。即使 values 是可变参数,但是它也只能改变一个属性,有时候我们需要缩放的同时变成半透明,有时候需要位移的时候顺便旋转一下,所以应运而生了 AnimatorSet 这个类,通过这个类我们可以将几个动画组合在一起,实现连续播放动画的效果。

AnimatorSet 有一个 play() 方法,需传入一个 Animator 对象,然后返回 AnimatorSet.Builder (没错,这是建造者模式),还可以在后面追加几个方法:

        public AnimatorSet.Builder with(Animator anim) {
            //play()传入的动画与该方法传入的动画同时播放
        }

        public AnimatorSet.Builder before(Animator anim) {
            //play()传入的动画在该方法传入的动画之前播放
        }

        public AnimatorSet.Builder after(Animator anim) {
            //play()传入的动画在该方法传入的动画之后播放
        }

        public AnimatorSet.Builder after(long delay) {
            //play()传入的方法在延迟该方法传入的时间(毫秒)后播放
        }


而 playSequentially(Animator... items) 则是顺序播放的方法,参数就是每一个 Animator。

了解了 AnimatorSet 的关键方法后我们就可以在客户端实现一个组合动画了:

                ObjectAnimator alpha = ObjectAnimator.ofFloat(ivBasketball, "alpha", 0, 1);
                ObjectAnimator rotation = ObjectAnimator.ofFloat(ivBasketball, "rotation", 0, 360);
                ObjectAnimator translationY = ObjectAnimator.ofFloat(ivBasketball, "translationY", 0, 500, 0);
                AnimatorSet mAnimatorSet = new AnimatorSet();
                mAnimatorSet.play(rotation).before(translationY).after(alpha);
                //这个动画时长是每个动画持续的时长而不是总时长
                mAnimatorSet.setDuration(2000);
                mAnimatorSet.start();
效果如下:

如果想先顺序播放两个动画之后,再会有同时播放的动画应该怎么办呢?看下面(我从我的小 Demo 中 copy 的代码,变量名有点复杂请不要在意):

        mAnimatorSet.play(arrowTurningPointYAnimator1).after(lineLengthAnimator);
        mAnimatorSet.play(linePointYAnimator).after(arrowTurningPointYAnimator1);
        mAnimatorSet.play(arrowTurningPointYAnimator2).after(arrowTurningPointYAnimator1);
        mAnimatorSet.play(arrowLengthAnimator).after(arrowTurningPointYAnimator2);
        mAnimatorSet.play(sweepAngleAnimator).after(arrowTurningPointYAnimator2);
        mAnimatorSet.play(tickTurningPointYAnimator).after(sweepAngleAnimator);
        mAnimatorSet.play(tickLengthAnimator).after(sweepAngleAnimator);
注意看每个变量名,上面代码的意思就是 arrowTurningPointYAnimator1 在 lineLengthAnimator 之后播放,然后 linePointYAnimator 在 arrowTurningPointYAnimator1 之后播放,arrowTurningPointYAnimator2 也在 arrowTurningPointYAnimator1 之后播放,所以 linePointYAnimator 和 arrowTurningPointYAnimator2 它俩是同时播放的,同理,后面是一样的,变量名有点复杂,可能看起来比较绕一点,谅解谅解。

5 TimeInterpolator

是一个接口,直译是时间插值器,它是用来决定动画播放速率的, Android 已经内置了很多时间插值器,列举几个:

AccelerateInterpolator:动画从开始到结束,变化率是一个加速的过程。

DecelerateInterpolator:动画从开始到结束,变化率是一个减速的过程。

CycleInterpolator:动画从开始到结束,变化率是循环给定次数的正弦曲线。

AccelerateDecelerateInterpolator:动画从开始到结束,变化率是先加速后减速的过程。

LinearInterpolator:动画从开始到结束,变化率是线性变化。

需要设置播放速率的时候只需要调用 setInterpolator() 方法即可:

                TimeInterpolator timeInterpolator = new AccelerateInterpolator(5f);
                translationY.setInterpolator(timeInterpolator);


可以看到在 tanslationY 动画播放的过程中,后半段明显比前半段快,这个类我还没大量用到,如果需要详解,可以移步郭神的博客:

http://blog.csdn.net/guolin_blog/article/details/44171115


6 TypeEvaluator

估值器,用来估算我们的动画执行到什么程度,什么时间该执行什么动画,说白了就是用来平滑过渡的, Android 也内置了很多估值器,比如FloatEvaluator,IntEvaluator和PointFEvaluator等,我们也可以自定义估值器,感觉要做复杂的动画就跟时间插值器和估值器分不开,现在我还没去看它。


属性动画也可以用xml代码写,但是得放到 /res/animator 文件夹下,这个文件夹默认没有,需要自己新建,将刚才的效果用xml代码写:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">
    <!-- 属性动画 Property Animation -->

    <!--android:ordering"sequentially" 这个动画集合里面的动画将顺序执行。 -->
    <!--android:ordering"together" 这个动画集合里面的动画将同时执行。 -->
    <objectAnimator
        android:duration="2000"
        android:propertyName="alpha"
        android:valueFrom="0.0"
        android:valueTo="1.0"
        android:valueType="floatType" />
    <objectAnimator
        android:duration="2000"
        android:propertyName="rotation"
        android:valueFrom="0.0"
        android:valueTo="360.0"
        android:valueType="floatType" />
    <set android:ordering="sequentially">
        <objectAnimator
            android:duration="1000"
            android:propertyName="translationY"
            android:valueFrom="0.0"
            android:valueTo="500.0"
            android:valueType="floatType" />
        <objectAnimator
            android:duration="1000"
            android:propertyName="translationY"
            android:valueFrom="500.0"
            android:valueTo="0.0"
            android:valueType="floatType" />
    </set>
</set>

然后在客户端调用:

                Animator animator = AnimatorInflater.loadAnimator(this, R.animator.property_animation);
                animator.setTarget(ivBasketball);
                animator.start();
运行后可以看到效果一样:


其实属性动画的基本用法也不难,只是要实现具体效果,还需要我们严密的逻辑代码,听说最近 Google 准备让 AS 支持 Koltin 了,没准还会抛弃 Java 呢,不过万变不离其宗,而且听说 Koltin 跟 Java 也差不太多。最近的求知欲正在上升期, Google 就给我搞这出,真是怕我学得太少啊(一点小牢骚)。

希望自己在编程的金字塔越爬越高。

所有动画效果完整的Demo:Demo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值