一.属性动画简介
属性动画非常强大,它的学习我是参考了这篇文章,博主写的非常独到、透彻、详细,在此感谢博主 Android属性动画完全解析(上),初识属性动画的基本用法 ,以下引用均出自该篇文章。
然而自Android 3.0版本开始,系统给我们提供了一种全新的动画模式,属性动画(property animation),它的功能非常强大,弥补了之前补间动画的一些缺陷,几乎是可以完全替代掉补间动画了。
Tween动画的缺点归纳(为什么诞生属性动画):
- 一旦我们的需求超出了移动、缩放、旋转和淡入淡出这四种对View的操作,那么补间动画就不能再帮我们忙了,也就是说它在功能和可扩展方面都有相当大的局限性,那么下面我们就来看看补间动画所不能胜任的场景。就是它只能够实现移动、缩放、旋转和淡入淡出这四种动画操作,那如果我们希望可以对View的背景色进行动态地改变呢?
- 介绍补间动画的时候都有使用“对View进行操作”这样的描述,没错,补间动画是只能够作用在View上的。也就是说,我们可以对一个Button、TextView、甚至是LinearLayout、或者其它任何继承自View的组件进行动画操作,但是如果我们想要对一个非View的对象进行动画操作,抱歉,补间动画就帮不上忙了
- 补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。
对于归纳出的缺点的第2点,可能不太让人理解,为什么对非View对象进行动画操作呢?
我怎么会需要对一个非View的对象进行动画操作呢?这里我举一个简单的例子,比如说我们有一个自定义的View,在这个View当中有一个Point对象用于管理坐标,然后在onDraw()方法当中就是根据这个Point对象的坐标值来进行绘制的。也就是说,如果我们可以对Point对象进行动画操作,那么整个自定义View的动画效果就有了。显然,补间动画是不具备这个功能的,这是它的一个缺陷。
对于归纳出的第3点,也让人疑问,什么叫没有改变View的属性
比如说,现在屏幕左上角有一个按钮,然后我们通过补间动画将它移动到了屏幕右下角,现在你可以去尝试点击一下这个处于屏幕右下角的按钮(动画执行过后的按钮),点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮绘制到了屏幕的右下角而已。也就是说Tween动画只是魔术,一切只是虚像。
惊喜的是属性动画完全解决了以上的问题。是不是很强大!!!!!但这不意味着我们抛弃了Tween动画。
二.属性动画整体结构
我们先不究其细节,现在整体初识它,这样学习起来更有全面感。它位于android.animation包下。
属性动画机制已经不再是针对于View来设计的了,也不限定于只能实现移动、缩放、旋转和淡入淡出这几种动画操作,同时也不再只是一种视觉上的动画效果了。它实际上是一种不断地对值进行操作的机制,并将值赋值到指定对象的指定属性上,可以是任意对象的任意属性。所以我们仍然可以将一个View进行移动或者缩放,但同时也可以对自定义View中的Point对象进行动画操作了。我们只需要告诉系统动画的运行时长,需要执行哪种类型的动画,以及动画的初始值和结束值,剩下的工作就可以全部交给系统去完成了。
工作原理
Property Animation动画有两个步聚:
1.计算属性值
2.为目标对象的属性设置属性值,即应用和刷新动画
XML的属性动画的应用
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);
animator.setTarget(view);
animator.start();
它有以下几个核心类以及对应的继承关系
三.属性动画细节分析
(1)ValueAnimator 类
1.介绍
属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等,确实是一个非常重要的类。
2.小案例
eg.有以下案例(但是这些动画无任何效果可看到,但是可以通过日志看到值变化情况)
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
//ValueAnimator anim1 = ValueAnimator.ofFloat(0f, 5f, 3f, 10f);
//ValueAnimator anim2 = ValueAnimator.ofInt(0, 100);
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();
(1)anim:将一个值从0平滑过渡到1,时长300毫秒,日志如下:
(2)anim1:将一个值在5秒内从0过渡到5,再过渡到3,再过渡到10
(3)anim2:当你不需要小数位数的动画过渡,可能你只是希望将一个整数值从0平滑地过渡到100,就可以用ofInt
讲解:
- 调用ValueAnimator的ofFloat()方法就可以构建出一个ValueAnimator的实例,ofFloat()方法当中允许传入多个float类型的参数,这里传入0和1就表示将值从0平滑过渡到1,然后调用ValueAnimator的setDuration()方法来设置动画运行的时长,最后调用start()方法启动动画。
- ValueAnimator当中最常用的应该就是ofFloat()和ofInt()这两个方法了,另外还有一个ofObject()方法。那么除此之外,我们还可以调用setStartDelay()方法来设置动画延迟播放的时间,调用setRepeatCount()和setRepeatMode()方法来设置动画循环播放的次数以及循环播放的模式,循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放的意思。
同时修改多个属性值
ObjectAnimator anim = ObjectAnimator.ofFloat(view, "xxx", 1.0F, 0.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);
}
});
4.对应XML的<animator>
实现一个从0到100平滑过渡的动画,在XML当中就可以这样写:
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="100"
android:valueType="intType"/>
(2)ObjectAnimator-可以直接对任意对象的任意属性进行动画操作
1.介绍
ValueAnimator只不过是对值进行了一个平滑的动画过渡,但是ObjectAnimator则就不同了,它是可以直接对任意对象的任意属性进行动画操作的,比如说View的alpha属性。但是它其实是继承自ValueAnimator的,底层的动画实现机制也是基于ValueAnimator来完成的,因此ValueAnimator仍然是整个属性动画当中最核心的一个类。那么既然是继承关系,说明ValueAnimator中可以使用的方法在ObjectAnimator中也是可以正常使用的,它们的用法也非常类似
2.小案例
eg.如果我们想要将一个TextView的某一个属性进行操作,就可以这样写:
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
//ObjectAnimator animator1 = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);
/**
float curTranslationX = textview.getTranslationX();
ObjectAnimator animator2 = ObjectAnimator.ofFloat(textview, "translationX", curTranslationX, -500f, curTranslationX);
**/
//ObjectAnimator animator3 = ObjectAnimator.ofFloat(textview, "scaleY", 1f, 3f, 1f);
animator.setDuration(5000);
animator.start();
(1)animator :TextView在5秒中内从常规变换成全透明,再从全透明变换成常规
(2)animator1 :TextView进行一次360度的旋转
(3)animator2 :TextView先向左移出屏幕,然后再移动回来
(4)animator3 :TextView在垂直方向上放大3倍再还原
3.讲解:
ObjectAnimator的ofFloat参数应该要填什么呢?也就是说修改TextView的透明度,为什么知道要填写“alpha”呢?就是要去查看一个参数对象TextView,里面是否提供set/get方法,比如TextView有setAlpha方法,有setTranslationX,那么对应第二个参数就填写该属性。其实这些方法是由View对象提供的,也就是说不仅TextView可以使用这个属性来进行淡入淡出动画操作,任何继承自View的对象都可以的。所以View方法里必然有那么View当中一定也存在着setRotation()、getRotation()、setTranslationX()、getTranslationX()、setScaleY()、getScaleY()这些方法
得出结论:
ObjectAnimator内部的工作机制是通过寻找特定属性的get和set方法,然后通过方法不断地对值进行改变,从而实现动画效果的。
4.对应XML的<objectAnimator>
将一个视图的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"/>
(3)AnimatorSet - 组合动画
1.介绍
实现组合动画功能主要需要借助AnimatorSet这个类,这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:
after(Animator anim) 将现有动画插入到传入的动画之后执行
after(long delay) 将现有动画延迟指定毫秒后执行
before(Animator anim) 将现有动画插入到传入的动画之前执行
with(Animator anim) 将现有动画和传入的动画同时执行
A.after(B):A要在B之后执行
A.before(B):A要在B之前执行
A.with(B):A和B同时执行
2.小案例
TextView先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作,就可以这样写:
ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(rotate).with(fadeInOut).after(moveIn);
animSet.setDuration(5000);
animSet.start();
3.讲解
可以看到,这里我们先是把三个动画的对象全部创建出来,然后new出一个AnimatorSet对象之后将这三个动画对象进行播放排序,让旋转和淡入淡出动画同时进行,并把它们插入到了平移动画的后面,最后是设置动画时长以及启动动画。
4.对应XML的<set>
将一个视图先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作,就可以这样写:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially" >
<objectAnimator
android:duration="2000"
android:propertyName="translationX"
android:valueFrom="-500"
android:valueTo="0"
android:valueType="floatType" >
</objectAnimator>
<set android:ordering="together" >
<objectAnimator
android:duration="3000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"
android:valueType="floatType" >
</objectAnimator>
<set android:ordering="sequentially" >
<objectAnimator
android:duration="1500"
android:propertyName="alpha"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType" >
</objectAnimator>
<objectAnimator
android:duration="1500"
android:propertyName="alpha"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType" >
</objectAnimator>
</set>
</set>
</set>
(4)Animator各种监听器
1.AnimatorListener
通过addListener()添加进去
anim.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
});
onAnimationStart()方法会在动画开始的时候调用,onAnimationRepeat()方法会在动画重复执行的时候调用,onAnimationEnd()方法会在动画结束的时候调用,onAnimationCancel()方法会在动画被取消的时候调用。
AnimatorListenerAdapter
也许很多时候我们并不想要监听那么多个事件,可能我只想要监听动画结束这一个事件,那么每次都要将四个接口全部实现一遍就显得非常繁琐。没关系,为此Android提供了一个适配器类,叫作AnimatorListenerAdapter,使用这个类就可以解决掉实现接口繁琐的问题了
anim.addListener(new AnimatorListenerAdapter() {
/**@Override
public void onAnimationEnd(Animator animation) {
} **/
});