android 属性动画

本文深入解析了Android 3.0版本引入的属性动画机制,对比传统补间动画的局限性,详细介绍了ValueAnimator与ObjectAnimator的功能及用法,包括动画组合与监听器的应用,并提供了XML编写动画的具体示例。

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

首先为什么要3.0引入属性动画呢?明显是因为传统的补间动画,已经远远满足不了咱们的开发需求了。

在为大家介绍属性动画之前,先讲一下补间动画的缺陷:

因为补间动画的机制就是使用硬编码的方式实现的,其扩展性极低。总分为以下几点:

1.补间动画只能作用于View上

2.补间动画改变的只是View 的显示效果而已(位移动画后,View的位置并没发生变化,View的单击事件还在原来的位置)

3.补间动画只能对View进行位移、缩放、旋转、淡入淡出这四种动画操作


也正是因为这些原因,Android开发团队决定在3.0版本当中引入属性动画这个功能,那么属性动画是不是就把上述的问题全部解决掉了?下面我们就来一起看一看。

新引入的属性动画已经不单单是对View的一些操作了。实际上属性动画就是通过不断的对值进行操作的机制,并将值赋值到对象的属性上。可以是任意对象的任意属性,当然我们还可以对View进行位移、缩放等动画操作,但是同时也可以对自定义View的Point对象进行动画操作。我们只需要简单的定义动画的运行时长、初始、结束值,什么类型的动画,剩下的就交给系统负责就行了。

看到这,相信大家对属性动画,已经有了简单的了解,那么接下来咱们就来看看属性动画的庐山真面目吧!

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();

看下log:


ValueAnimator里面用到最多的应该是 ofFloat()和ofInt()这两个方法了,另外还有一个ofObject()方法,我会在下篇文章里面进行讲解。

另外我们还可以调用setStartDelay()方法来设置动画延迟播放的时间,调用setRepeatCount()setRepeatMode()方法来设置动画循环的次数以及循环播放的模式,循环模式包括RESTARTREVERSE两种,分别表示重新播放倒序播放

ObjectAnimator

相对于ValueAnimator,ObjectAnimator可能才是我们接触次数最多的类,因为ValueAnimator只不过是对值进行了一个平滑的动画过渡,使用场景好像不是很多,而ObjectAnimator是直接对任意对象的任意属性进行动画操作的。比如View的alpha属性。

ObjectAnimator其实继承自ValueAnimator,底层的动画实现机制也是基于ValueAnimator来完成的,因为ValueAnimator仍然是整个属性动画中最核心的一个类。既然是继承关系,那么ValueAnimator的方法在ObjectAnimator中也是可以正常使用的,他们的用法也非常类似,这里如果我们想要将一个View在6秒钟从常规变换成全透明,在从全透明变成常规的,就可以这么写:

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(text,"alpha",1f,0f,1f);
objectAnimator.setDuration(6000);
objectAnimator.start();

第一个参数是view对象,第二个参数我传入了"alpha"是因为要改变view的不透明度。

学到这里,我们就可以举一反三了,比如将view进行一次360度的旋转:

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(text,"rotation",0f,360f);
objectAnimator.setDuration(6000);
objectAnimator.start();

可以看到,这里我们将第二个参数改成了"rotation"

比如将view左移出屏幕,在移动回来:

float curTranslationX = text.getTranslationX();
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(text,"translationX",curTranslationX,-500f,curTranslationX);
objectAnimator.setDuration(6000);
objectAnimator.start();

这里我们先是调用了getTranslationX()方法来获取到当前view的translationX的位置,然后ofFloat()方法的第二个参数传入"translationX",紧接着后面三个参数用于告诉系统view应该怎么移动

比如缩放操作,放大5倍在还原:

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(text,"scaleY",1f,3f,1f);
objectAnimator.setDuration(6000);
objectAnimator.start();

这里将ofFloat()方法的第二个参数改成了"scaleY"

到目前为止,相信不少朋友心里会有一个疑问,究竟ofFloat()中的第二个参数都能传哪些值呢?其实是任意的值因为ObjectAnimator在设计的时候就没有针对于View来进行设计,而是针对于任意对象的,它所负责的工作就是不断地向某个对象中的某个属性进行赋值,然后对象根据属性值的改变再来决定如何展现出来

比如这段代码:

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(text,"alpha",1f,0f,1f);

textview对象中有没有这个alpha属性这个值呢?没有,不仅textview没有这个值,连他的父类也是没有这个属性的。那么ObjectAnimator是怎么操作的呢?其实ObjectAnimator内部的工作机制并不是直接对我们传入的属性名进行操作的,而是去寻找这个属性对应的get和set方法,因此alpha属性所对应的get和set方法应该就是:

  1. public void setAlpha(float value);  
  2. public float getAlpha();  

那么textview对象中有没有这两个方法?确实有,并且这两个方法是由View对象提供的,也就是说不仅TextView可以使用这个属性来进行淡入淡出动画操作,任何继承自View的对象都是可以的。

既然alpha是这样,相信大家看到这里已经明白了吧,前面我们所用的所有属性都是这个原理,那么View当中一定也存在setRotation()、getRotation()、setTranslationX()、getTranslationX()、setScaleY()、getScaleY()这些方法的。大家有时间可以探究一下。

组合动画

独立的动画实现的视觉效果也是有限的,因此将多个动画组合到一起就显得尤为重要。Android团队也提供了一套非常丰富的API来将多个动画组合到一起。

AnimatorSet 

这个类提供了一个play()方法,向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimato)将会返回一个AnimatorSet.Builder的实例,包括四个方法:

  • after(Animator anim)   将现有动画插入到传入的动画之后执行
  • after(long delay)   将现有动画延迟指定毫秒后执行
  • before(Animator anim)   将现有动画插入到传入的动画之前执行
  • with(Animator anim)   将现有动画和传入的动画同时执行
有了这四个方法,就可以完成组合动画的逻辑了,比如先让view从屏幕外移动进屏幕,然后旋转360度,旋转的同时进行淡入淡出操作,就可以这么写:

ObjectAnimator moveIn = ObjectAnimator.ofFloat(text, "translationX", -500f, 0f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(text, "rotation", 0f, 360f);
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(text, "alpha", 1f, 0f, 1f);
AnimatorSet set = new AnimatorSet();
set.play(rotate).with(fadeInOut).after(moveIn);
set.setDuration(5000);
set.start();
先把三个动画的对象全部创建出来,然后new一个 AnimatorSet对象,然后对这三个动画对象进行排序,让旋转和淡入淡出动画同时进行,并把它们插入到平移动画的后面,最后设置动画时长以及启动动画。

Animator监听器

实现AnimatorListener去监听动画的各种事件。

大家知道,ObjectAnimator是继承自ValueAnimator的,而ValueAnimator又是继承自Animator的,因此不管是ValueAnimator还是ObjectAnimator都是可以使用addListener()这个方法的。另外AnimatorSet也是继承自Animator的。所以addListener()这个方法是个通用的方法。

添加一个监听器:

set.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animation) {
        
    }
    @Override
    public void onAnimationEnd(Animator animation) {

    }
    @Override
    public void onAnimationCancel(Animator animation) {

    }
    @Override
    public void onAnimationRepeat(Animator animation) {

    }
});

onAnimationStart()方法会在动画开始的时候调用,onAnimationRepeat()方法会在动画重复执行的时候调用,onAnimationEnd()方法会在动画结束的时候调用,onAnimationCancel()方法会在动画被取消的时候调用。

也许有时候我们并不需要监听那么多个事件,只监听动画结束的时候,AnimatorListenerAdapter,使用这个类就可以解决实现接口繁琐的问题了:

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

可以手动选择你需要监听的事件。

使用XML编写动画

如果想要使用XML来编写动画,首先要在res目录下面新建一个animator文件夹,所有属性动画的XML文件都应该存放在这个文件夹当中。然后在XML文件中我们一共可以使用如下三种标签:

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

那么比如说我们想要实现一个从0到100平滑过渡的动画,在XML当中就可以这样写:

  1. <animator xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:valueFrom="0"  
  3.     android:valueTo="100"  
  4.     android:valueType="intType"/>  
而如果我们想将一个视图的alpha属性从1变成0,就可以这样写:
[html]  view plain  copy
  1. <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:valueFrom="1"  
  3.     android:valueTo="0"  
  4.     android:valueType="floatType"  
  5.     android:propertyName="alpha"/>  
其实XML编写动画在可读性方面还是挺高的,上面的内容相信不用我做解释大家也都看得懂吧。


另外,我们也可以使用XML来完成复杂的组合动画操作,比如将一个视图先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作,就可以这样写:

[html]  view plain  copy
  1. <set xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:ordering="sequentially" >  
  3.   
  4.     <objectAnimator  
  5.         android:duration="2000"  
  6.         android:propertyName="translationX"  
  7.         android:valueFrom="-500"  
  8.         android:valueTo="0"  
  9.         android:valueType="floatType" >  
  10.     </objectAnimator>  
  11.   
  12.     <set android:ordering="together" >  
  13.         <objectAnimator  
  14.             android:duration="3000"  
  15.             android:propertyName="rotation"  
  16.             android:valueFrom="0"  
  17.             android:valueTo="360"  
  18.             android:valueType="floatType" >  
  19.         </objectAnimator>  
  20.   
  21.         <set android:ordering="sequentially" >  
  22.             <objectAnimator  
  23.                 android:duration="1500"  
  24.                 android:propertyName="alpha"  
  25.                 android:valueFrom="1"  
  26.                 android:valueTo="0"  
  27.                 android:valueType="floatType" >  
  28.             </objectAnimator>  
  29.             <objectAnimator  
  30.                 android:duration="1500"  
  31.                 android:propertyName="alpha"  
  32.                 android:valueFrom="0"  
  33.                 android:valueTo="1"  
  34.                 android:valueType="floatType" >  
  35.             </objectAnimator>  
  36.         </set>  
  37.     </set>  
  38.   
  39. </set>  
这段XML实现的效果和我们刚才通过代码来实现的组合动画的效果是一模一样的,每个参数的含义都非常清楚,相信大家都是一看就懂,我就不再一一解释了。


最后XML文件是编写好了,那么我们如何在代码中把文件加载进来并将动画启动呢?只需调用如下代码即可:

[java]  view plain  copy
  1. Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);  
  2. animator.setTarget(view);  
  3. animator.start();  
调用AnimatorInflater的loadAnimator来将XML动画文件加载进来,然后再调用setTarget()方法将这个动画设置到某一个对象上面,最后再调用start()方法启动动画就可以了,就是这么简单



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值