Android属性动画完全解析(一)

本文深入探讨了Android属性动画机制,包括ValueAnimator与ObjectAnimator的区别及使用方法,如何创建组合动画,以及XML方式定义动画等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.引言

早期Android提供了两种动画方式,逐帧动画(frame-by-frame animation)和补间动画(tweened animation)。逐帧动画的工作原理如同动画片一样,通过一张张单独的图片连贯起来播放。补间动画则是可以对View进行一系列的动画操作,包括淡入淡出、缩放、平移、旋转四种。

这里就会有人有疑问了,然而我们还是要看到这两种动画的一些限制,犹其是补间动画这种非常常用的动画,在功能和可扩展方面还是有相当大的局限性,主要体现在以下三个方面:

  1. 补间动画是只能够作用在View上的,无法非View的对象进行动画操作。这是什么意思呢?举个例子,比如说我们有一个自定义的View,在这个View当中有一个Point对象用于管理坐标,然后在onDraw()方法当中就是根据这个Point对象的坐标值来进行绘制的。也就是说,如果我们可以对Point对象进行动画操作,那么整个自定义View的动画效果就有了。显然,补间动画是不具备这个功能的,这是它的第一个缺陷。

  2. 补间动画机制就是使用硬编码的方式来完成的,功能上无法扩展。比如想让view的背景色变换,它是没办法做到的

  3. 补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。什么意思呢?比如说,现在屏幕的左上角有一个按钮,然后我们通过补间动画将它移动到了屏幕的右下角,现在你可以去尝试点击一下这个按钮,点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮绘制到了屏幕的右下角而已。

为了解决上述的问题,Android团队推出了新的动画机制:Property Animation,也就是属性动画。

二、ValueAnimator

在我们的动画机制中,有两个类是比较基本的,一个是ValueAnimator,一个是ObjectAnimator,前面一个是基类,后一个是子类,但使用更为方便,也很常用。
ValueAnimator是整个属性动画机制当中最核心的一个类,前面我们已经提到了,属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等,确实是一个非常重要的类。

注意,ValueAnimator操作的只是值的变化,我们可以为看下面这个例子:

ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);  
anim.setDuration(300);  
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(); 

这个里面我们进行的操作主要是将数值0到1进行变化,变化的时间为300毫秒,而且设置了一个updateListener,来监听并打印数值的变化。可以料到,程序运行的结果就是在300毫秒内,打印了一串0-1之间的数。

但是说的是属性动作,只模拟了数值的变化怎么行。是的,ValueAnimator只是将数值进行操作,本身并没有涉及到属性。在使用中需要配合属性来进行。
它的好处在于:不必针对某个单一的属性,你可以自己根据当前动画的计算值,来操作任何属性,只要这个属性有getter,setter,比如【希望一个动画能够让View既可以放大、背景又能够由浅变深(3个属性scaleX,scaleY,alpha)】,就可以利用上述构造出来的anim,直接设置:

//利用单个ValueAnimator,对多个属性进行操作
View.setScalX(anim);
View.setScalY(anim);
View.setAlpha(anim);

这里就不得不扯一下ObjectAnimator的一个非典型用法:

public void rotateyAnimRun(final View view)  
{  
    ObjectAnimator anim = ObjectAnimator//  
            .ofFloat(view, "suibian", 0.0F,  1.0F)//  
            .setDuration(500);//  
    anim.start();  
    anim.addUpdateListener(new AnimatorUpdateListener()  
    {  
        @Override  
        public void onAnimationUpdate(ValueAnimator animation)  
        {  
            float cVal = (Float) animation.getAnimatedValue();  
            view.setAlpha(cVal);  
            view.setScaleX(cVal);  
            view.setScaleY(cVal);  
        }  
    });  
}  

这一串代码用了ObjectAnimator,实现的功能和前面用ValueAnimator一模一样,为啥呢?主要看这一句。
ObjectAnimator anim = ObjectAnimator//
.ofFloat(view, “suibian”, 0.0F, 1.0F)//
.setDuration(500);
解释一样,view就是我们要操作的对象,第二个参数是属性,看到我们传了一个”suibian ”进去,“suibian”并不是这个对象有的属性,于是ObjectAnimator就退化为ValueAnimator对第三个第四个参数进行值操作,使用是就需要利用属性的setter方法了。

既然说到了操作多个动画,这里再提另外一种方法:使用propertyValuesHolder,听名字就可以猜到用法

public void propertyValuesHolder(View view)  
    {  
        PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,  
                0f, 1f);  
        PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,  
                0, 1f);  
        PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,  
                0, 1f);  
        ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY,pvhZ).setDuration(1000).start();  
    }  

三、ObjectAnimator

谈起ObjectAnimator,还是要对比ValueAnimator.ObjectAnimator就直观的特点就是可以直接对任意对象的任意属性进行动画操作的,比如说View的alpha属性。
但说到继承关系,ObjectAnimator归根结底调用的都是ValueAnimator,所以呢,其实ObjectAnimator能够实现的用ValueAnimator照样能够实现。
由于参数比较直观,我直接用一个例子来展示了:

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "scaleX", 1.0f, 0.5f,1.0f);  
animator.setDuration(5000);  
animator.start();  

可以看出,我们对一个textView在X轴方向上进行缩放操作,与前面的不同,这里我们传了了三个参数,1.0f—>0.5f—>1.0f,对应的效果就是textView在x方向上先缩小到一半,再放大到原来大小。

四、组合动画

到这里,有读者可能想问,如果有多个动画,而且动画的播放有一个先后顺序怎么办。嗯,Android团队肯定也想到这个问题了。
实现组合动画功能主要需要借助AnimatorSet这个类,这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:

after(Animator anim)   将现有动画插入到传入的动画之后执行
after(long delay)   将现有动画延迟指定毫秒后执行
before(Animator anim)   将现有动画插入到传入的动画之前执行
with(Animator anim)   将现有动画和传入的动画同时执行

直接上代码:

 public void playWithAfter(View view)  
    {  
        float cx = mBlueBall.getX();  

        ObjectAnimator anim1 = ObjectAnimator.ofFloat(mBlueBall, "scaleX",  
                1.0f, 2f);  
        ObjectAnimator anim2 = ObjectAnimator.ofFloat(mBlueBall, "scaleY",  
                1.0f, 2f);  
        ObjectAnimator anim3 = ObjectAnimator.ofFloat(mBlueBall,  
                "x",  cx ,  0f);  
        ObjectAnimator anim4 = ObjectAnimator.ofFloat(mBlueBall,  
                "x", cx);  

        /** 
         * anim1,anim2,anim3同时执行 
         * anim4接着执行 
         */  
        AnimatorSet animSet = new AnimatorSet();  
        animSet.play(anim1).with(anim2);  
        animSet.play(anim2).with(anim3);  
        animSet.play(anim4).after(anim3);  
        animSet.setDuration(1000);  
        animSet.start();  
    }  
}  

那一串的playwith,playafter就是执行的先后。虽然是支持链式编程的,但是还是要谨慎的,不要挤在一起写,一句就写完,分开来写比较好。

最后提一下监听器,addListener里面总共实现了四个接口,onAnimationStart()方法会在动画开始的时候调用,onAnimationRepeat()方法会在动画重复执行的时候调用,onAnimationEnd()方法会在动画结束的时候调用,onAnimationCancel()方法会在动画被取消的时候调用。
有人会觉得挺麻烦的,没关系,Android提供了一个适配器类,叫作AnimatorListenerAdapter,使用这个类就可以解决掉实现接口繁琐的问题了,可以实现自己需要的接口:

anim.addListener(new AnimatorListenerAdapter() {  
    @Override  
    public void onAnimationEnd(Animator animation) {  
    }  
});  

在xml中实现动画的方式也是极其简单的,我们可以在res目录下面新建一个animator文件夹,所有属性动画的XML文件都存放在这个文件夹当中。
XML文件中主要有三种标签:

<animator>  对应代码中的ValueAnimator
<objectAnimator>  对应代码中的ObjectAnimator
<set>  对应代码中的AnimatorSet

而如果我们想将一个视图的alpha属性从1变成0,就可以这样写:

<objectAnimator     xmlns:android="http://schemas.android.com/apk/res/android"
    android:valueFrom="1"  
    android:valueTo="0"  
    android:valueType="floatType"  
    android:propertyName="alpha"/>  

然后怎么在代码中调起这段动画呢,一句话搞定

Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);  
animator.setTarget(view);  
animator.start();

loadAnimator进来,和使用layout很类似有木有,就是这么简单。
有兴趣的可以去看看鸿洋用动画实现的线:
http://blog.youkuaiyun.com/lmj623565791/article/details/38067475

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值