文章目录
一、属性动画(Animator)介绍
属性动画系统是一个强健的框架,用于为几乎任何内容添加动画效果。——亲爸爸谷歌
我们知道,在Android中,补间动画对应的类为Animation,而属性动画对应的类则为Animator。
属性动画是Android 3.0才引入的,相对于补间动画是比较新的存在。
属性动画顾名思义,是指改变某个属性,来达到实现动画的目的。例如,我想改变View的透明度,用补间动画的做法,就是创建一个AlphaAnimation,然后交给View开始动画,而属性动画则直接操控View的alpha属性,在一个时间段内,变化alpha的值,来使得View的透明度发生变化。
这里放出Animator的继承关系图,现在脑海里留个印象:
二、ValueAnimator
属性动画的主计时引擎,它也可计算要添加动画效果的属性的值。它具有计算动画值所需的所有核心功能,同时包含每个动画的计时详情、有关动画是否重复播放的信息、用于接收更新事件的监听器以及设置待评估自定义类型的功能。——亲爸爸谷歌
就以透明动画为例,我们使用ValueAnimator来创建这样一个动画。
1、创建ValueAnimator实例
这边直接new就行了。
val anim = ValueAnimator()
2、设置动画的相关属性
作为一个动画,肯定有播放时长吧:
anim.duration = 1000
作为一个ValueAnimator,肯定有Value吧!
根据谷歌爸爸提供的信息:“它也可计算要添加动画效果的属性的值”。
那么我们就要添加动画效果的属性的值……
回到出发点,我们是要改变View的透明度,那么就是设置他的透明度属性,那透明度属性的值肯定就是我们需要添加的!
View.setAlpha(float value),里面的value取值范围0到1,
0就是完全透明,1就是完全不透明。那还等什么,赶紧设置值吧,高兴的我赶紧找到anim.setFloatValue()
方法,又呆滞了:
public void setFloatValues(float... values)
这个方法里面的参数是可变参数啊,那么我们到底要传什么值呢?
这里直接揭露吧:
- 如果只传一个值,那么动画会默认从0到这个值的过程中,计算生成一直在变化的值。
- 如果传两个值,那么这段时间,动画会计算生成从第一个值到第二个值的过程中,一直在变化的值。
- 传入多个值,那么就会分多个阶段计算,得到计算之后的值。
3、值已经设置好了,队长,下一步怎么办?
anim.setFloatValues(0f,1f)
接下来就是将anim和View“关联起来”:
anim.addUpdateListener {valueAnimator->
animatorView.alpha = valueAnimator.animatedValue as Float
}
通过对动画设置“更新监听”,使得每一次更新的时候,拿到其animatedValue就是计算后的值,在本例中这个值就是从0到1之间的数。
完整代码如下:
val anim = ValueAnimator()
anim.setFloatValues(0f,1f)
anim.duration = 1000
anim.addUpdateListener {valueAnimator->
animatorView.alpha = valueAnimator.animatedValue as Float
}
btn_start.setOnClickListener {
anim.start()
}
4、改进
- 事实上,ValueAnimator给我们提供了多个静态方法,来帮助我们直接创建带有值的实例:
val anim = ValueAnimator.ofFloat(0f,1f)
- 我们记得不管是逐帧动画还是补间动画,都可以定义xml动画资源,那么属性动画也是可以的。需要在res创建animator目录,然后在其目录中创建xml资源文件。
ValueAnimator 对应的xml标签为 <animator>,如下所示:
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:duration="1000">
</animator>
然后通过AnimatorInflater.loadAnimator()
方法加载资源文件
val animFromXML = AnimatorInflater.loadAnimator(context,R.animator.anim_alpha) as ValueAnimator
animFromXML.addUpdateListener {
animatorView.alpha = it.animatedValue as Float
}
三、ObjectAnimator
ValueAnimator 的子类,用于设置目标对象和对象属性以添加动画效果。此类会在计算出动画的新值后相应地更新属性。——亲爸爸谷歌
我们还是先从它的实现开始吧。还是以透明动画为例,我们使用ObjectAnimator来创建这样一个动画。
1、创建ObjectAnimator实例
val objectAnimator = ObjectAnimator()
//顺便把时间设置好
objectAnimator.duration = 1000
2、设置属性
根据谷歌的文档,作为一个ObjectAnimator,需要设一个目标对象和目标对象的属性,设置目标对象很少理解,直接就是我们需要展示动画的View:
objectAnimator.target = animatorView
objectAnimator.setPropertyName("alpha")
可是目标属性呢?
要添加动画效果的对象属性必须具有 set<PropertyName>() 形式的 setter 函数(采用驼峰式大小写形式)。——亲爸爸谷歌
好在View的设置透明度的方法符合这样的要求:setAlpha(),那么PropertyName就是“Alpha”了:
objectAnimator.setPropertyName("Alpha")
最后设置值的范围:
objectAnimator.setFloatValues(0f,1f)
原所有代码:
val objectAnimator = ObjectAnimator()
objectAnimator.duration = 1000
objectAnimator.target = animatorView
objectAnimator.setPropertyName("Alpha")
objectAnimator.setFloatValues(0f,1f)
btn_start.setOnClickListener {
objectAnimator.start()
}
效果就不展示了,跟之前的一模一样。
3、怎样的属性才能设置?(重点)
前面也说了,我们必须遵守其规则,标准的setter方法,必须采用驼峰命名法!
- 注意:如果我们设置value的时候,只设置一个value,它就会认定为这个值是结束值,那么初始值就会通过getter方法来获取。如果没有这个getter方法的话,否则out。
- 注意:如果有用到getter方法的话,其返回值的类型必须和setter方法参数类型一致,否则out。
- 如果你的Target对象没有setter或者getter,很囧怎么办?
解决办法:创建一个ViewHolder类,这个类持有这个View,然后手动添加setter和getter。
class ViewHolder(val view:View){
fun setPropertyName(value:Float){
view.alpha = value
}
fun getPropertyName(): Float {
return view.alpha
}
}
val viewHolder= ViewHolder(animatorView)
//ObjectAnimator
val objectAnimator = ObjectAnimator.ofObject(viewHolder,"PropertyName",FloatEvaluator(),0f)
- 别耍小聪明~不支持Kotlin扩展方法。
4、改进
- ObjectAnimator也提供了多个静态方法,来一次性初始化若干个动画属性:
val objectAnimator =
ObjectAnimator.ofObject(animatorView,"Alpha",FloatEvaluator(),0f,1f)
我们需要添加一个Evaluator对象,这是一个估值器,下一章会有介绍。
在这里我们传入的Value类型是Float类型的,所以就传入FloatEvaluator,如果是int类型,就传入IntEvaluator,这些基本的估值器系统都有提供。
- 我们同样也能够生命xml的ObjectAnimator动画资源文件,其对应的xml标签为 <objectAnimator>:
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="Alpha"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:duration="1000">
</objectAnimator>
然后在代码中初始化,注意添加target对象:
val objectFromXml = AnimatorInflater.loadAnimator(context,R.animator.anim_object) as ObjectAnimator
objectFromXml.target = animatorView
四、AnimatorSet
在许多情况下,您需要根据一个动画开始或结束的时间来播放另一个动画。借助 Android 系统,您可以将动画捆绑到一个 AnimatorSet 中,以便指定是同时播放动画、按顺序播放还是在指定的延迟时间后播放。您还可以相互嵌套 AnimatorSet 对象。——亲爸爸谷歌
1、方法一
上面这一段话已经描述的很清楚了,我们直接看如何实现:
//假设有两个Animator anim1和anim2
val animatorSet = AnimatorSet()
//直接播放 anim1
animatorSet.play(anim1)
//播放完 anim1 紧接着播放 anim2
animatorSet.play(anim1).after(anim2)
//在播放anim1 之前先播放 anim2
animatorSet.play(anim1).before(anim2)
//同时播放 anim1 和 anim2
animatorSet.play(anim1).with(anim2)
//播放完 anim1 等过了1000ms之后再播放 anim2
animatorSet.play(anim1).after(1000).after(anim2)
//同时播放 anim1 和 anim2
animatorSet.playTogether(anim1,anim2)
//最后最后,需要调用 start()方法开始动画
animatorSet.start()
2、方法二
当然,我们依然可以定义一个AnimatorSet的xml动画资源,其对应的xml标签为 <set>:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:propertyName="alpha"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:duration="1000" />
<objectAnimator
android:propertyName="TranslationX"
android:valueFrom="0"
android:valueTo="100"
android:valueType="floatType"
android:duration="1000"/>
</set>
然后再代码中初始化:
val setFromXml = AnimatorInflater.loadAnimator(context,R.animator.anim_set) as AnimatorSet
setFromXml.setTarget(animatorView)
效果如图所示:
3、注意点
- 在 <set> 里面可以设置 <animator>、<objectAnimator> 和 <set> 三个子标签。
- 在被 <set> 标签包裹的动画,表示会同时播放。
- 在构建 <set> 的时候,尽量不要使用 <animator>,因为你还需要设置其更新监听,然后再在里面添加方法。在上面例子中,如果将第一个 <objectAnimator> 改成 <animator>,那么你还需要在代码中添加如下代码:
val anim1 = setFromXml.childAnimations[0] as ValueAnimator
anim1.addUpdateListener { valueAnimator
animatorView.alpha = valueAnimator.animatedValue as Float
}
这样的话得不偿失,反而更加麻烦。
五、动画监听
在实际项目中,我们可能需要在动画的某个节点来做某些事情,熟悉他们的监听时很有必要的事情。
1、Animator
Animator作为超类,它拥有的两个监听:
- Animator.AnimatorListener
– onAnimationStart() - 在动画开始播放时调用。
– onAnimationEnd() - 在动画结束播放时调用。
– onAnimationRepeat() - 在动画重复播放时调用。
– onAnimationCancel() - 在动画取消播放时调用。
取消的动画也会调用onAnimationEnd(),无论它们以何种方式结束。 - Animator.AnimatorPauseListener
– onAnimationPause() - 在动画暂停时调用。
– onAnimationResume() - 在动画暂停之后,继续播放时调用。
2、ValueAnimator
ValueAnimator自身只有一个监听:
- ValueAnimator.AnimatorUpdateListener
– onAnimationUpdate() - 对动画的每一帧调用。