Property Animation是通过改变对象的属性值来创建动画的机制。尽管View Animation提供了Tween Animation 和 Frame Animation这些动画能解决我们开发过程中常用的大多数动画特效,然后View Animation还是有很多的限制,比如Tween Animation 仅仅支持平移、缩放等这些操作,Frame Animation 是直接对多张图的逐帧播放,这些都收到很大的限制,然而在Android 3.0之后, google 提出了 Property Animation,顾名思义,我们可以使用Property Animation 对对象的属性创建动画,比如View 对象的背景颜色、水平方向垂直方向的位置、以及View提供的其他属性,可以说Property Animation 几乎无所不能,借下来这篇文章我将带领大家了解Property Animation 到底是如何实现的。
下面让我们先来看一下Property是如何进行运动的:
图片1-匀速运动
上图一展示的是一个我们假设的对象在水平方向上移动了40个px,花费了40ms.以10ms刷新一次动画,四次10ms都运动了10px,采用的是线性匀速运动,以相同的速率进行移动。
img
上图二展示了非线形运动的图示。以水平方向开始和结束位置相同,同样时间内采用不用的速率进行移动。从图中我们可以看出,以小x=20中间点为例,该动画从起点加速到中间点,然后从中间点再以减速运动移动。
下面我们再来讨论下 property Animation 系统是如何通过列出的重要组件进行计算的
[外链图片转存失败(img-JooCXZ48-1563449081754)(https://developer.android.com/images/animation/valueanimator.png)]
上图三列出了一些property Animation的重要组件:
比如,在图二中使用的TimeInterpolator是AccelerateDecelerateInterpolator,使用的TypeEvaluator是系统定义好的IntEvaluator。
当我们创建一个Property Animation时,创建一个ValueAnimatior对象,提供一个开始和结束值、动画持续时间。当调用ValueAnimatior.start()动画开始,在整个动画过程中ValueAnimator计算过去时分数,过去时分数介于0-1之间、是通过经过的时间/动画持续的时间来计算的。分数因子类比百分比数,0等于0%,1等价于100%。比如图一在10ms的时候分数因子为10/40=.25.
ValueAnimator计算出过去时分数后,它会调用当前设置的TimeInterpolator来计算插值分数。插值分数是通过输入过去时分数这个变量,通过当前设置的TimeInterpolator公式进行计算的。比如图二中,因为物体会缓慢加速,所以在10ms内插值分数为.15少于.25,而图一是以一种匀速运动运动一直运动,插值分数不会改变。
当插值分数计算出来后,ValueAnimator会调用合适的TypeEvaluator进行计算当前动画的属性值,计算变量为插值分数、开始值、结束值。比如图二中,在10ms的时候插值分数为.15,所以当前动画的属性值为.15*(40-0).
完成整个计算动画的流程后,我们进行整理:
PropertyAnimation创建动画是通过两个步骤来走的:
从Property Animation是如何工作的一章我们了解到,ValueAnimator完成了第一步,而第二步需要我们编写逻辑来实现,使用ValueAnimator的流程如下:
下面我们列出代码示例:
ValueAnimator.ofFloat(0f, 100f).apply {
//设置动画持续时间
duration = 4000
//设置重复次数
repeatCount=10
//重复模式
repeatMode=ValueAnimator.REVERSE
// 设置加速插值器
interpolator=AccelerateInterpolator()
addUpdateListener { updateAnimation ->
//改变目标的属性值
iv_image.translationX = (updateAnimation.animatedValue as Float)
}
start()
}
代码中,展示了一个简单以加速运动在水平方向从0平移100的动画特效,持续4000模式,重复10次。
ObjectAnimator是ValueAnimation的子类,它对ValueAnimation进一步封装,主要封装了对目标对象设置改变的属性值,以使得创建属性动画的代码更加简洁、开发速度更快速。
还是之前的平移动画效果,我们看看使用ObjectAnmator是如何实现上面的代码的:
ObjectAnimator.ofFloat(iv_image, "translationX", 0f, 100f).apply {
duration = 4000
repeatCount=10
//设置重复次数
repeatMode=ValueAnimator.REVERSE
//设置加速插值器
interpolator=AccelerateInterpolator()
start()
}
代码和之前的有点类似,也是使用工厂方法创建ObjectAnimator传入目标对象,想要修改的属性。ObjectAnimator注重于帮助我们设置改变的属性,不再需要编写addUpdateListener中的代码,极大的增加了代码的简洁性。
在使用ObjectAnimator我们必须确保一些前置条件,否则属性动画将无法设置属性:
有时候我们需要对动画进行组合,AnimatorSet就能把几个单个动画特效整合成一个组合动画,并且可以控制单个动画的执行顺序。
AnimatorSet是Animator的子类,控制者动画集合,根据Animator提供的API控制顺序:
代码示例:
val translateAnimation = ObjectAnimator.ofFloat(iv_image, "translationX", 0f, 100f).apply {
duration = 1000
}
val translateAnimation1 = ObjectAnimator.ofFloat(iv_image, "translationX", 0f, 200f).apply {
duration = 1000
}
val translateAnimation2 = ObjectAnimator.ofFloat(iv_image, "translationX", 0f, 300f).apply {
duration = 1000
}
val scaleXAnimator = ObjectAnimator.ofFloat(iv_image, "scaleX", 0f, 1f).apply {
duration = 1000
}
// 创建AnimationSet
val boundAnimbouncer = AnimatorSet().apply {
// translateAnimation在scaleXAnimator之前执行
play(translateAnimation).before(scaleXAnimator)
// translateAnimation在translateAnimation1之后执行
play(translateAnimation).after(translateAnimation1)
// translateAnimation和translateAnimation1一起执行
play(translateAnimation).with(translateAnimation2)
}
// 创建透明度改变动画
val faceAnim = ObjectAnimator.ofFloat(iv_image, "alpha", 0f, 1f).apply {
duration = 5000
}
AnimatorSet().apply {
//faceAnim在boundAnimbouncer之前执行
play(faceAnim).before(boundAnimbouncer)
start()
}
在使用AnimatorSet之前,我们需要创建单独的动画效果:
translateAnimation平移100、translateAnimation1平移200、translateAnimation2平移300、scaleXAnimator水平方向缩放,faceAnim透明度改变动画。
在我们设置的执行顺序时,真实的动画效果顺序应当以如下顺序执行:
你可以使用**Animator.AnimatorListener()**监听在动画过程中一些重要的事件:
你可以使用**ValueAnimator.AnimatorUpdateListener()**监听动画的每一帧。在动画过程中是使用ValueAnimator对象来计算的。为了使用计算值,你可以通过ValueAnimator.getAnimatedValue()获得当前动画计算的值。更多的详情可以查看-ValueAnimator一章。
如果你不想实现所有的Animator.AnimatorListener()接口方法,你也可以继承AnimatorListenerAdapter替代**Animator.AnimatorListener()接口。AnimatorListenerAdapter提供了实现了Animator.AnimatorListener()**接口但是提供了一些空实现的方法。
比如,我们可以仅仅实现一个onAnimationEnd()方法的代码示例:
ValueAnimator.ofFloat(0f, 100f).apply {
//设置动画持续时间
duration = 4000
repeatCount = 10
//设置重复次数
repeatMode = ValueAnimator.REVERSE
//设置加速插值器
interpolator = AccelerateInterpolator()
addUpdateListener { updateAnimation ->
//改变目标的属性值
iv_image.translationX = (updateAnimation.animatedValue as Float)
}
//添加一个重要事件的监听
addListener(object:AnimatorListenerAdapter(){
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
iv_image.visibility=View.GONE
}
})
start()
}
如果安卓系统无法识别你当前想要转换的动画类型值,你可以通过实现TypeEvaluator实现**evaluate()**来自定义动画计算值。安卓系统为int,float和color这些类型提供了支持类IntEvaluator,FloatEvaluator,ArgbEvaluator.
在开始自定义TypeEvaluator之前,我们先来看看源码中定义的Float类型的FloatEvaluator:
public class FloatEvaluator implements TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}
FloatEvaluator的实现很简单,它就是根据fraction插值分数,计算当前动画的值。
为了更好的理解Property Animation 是如何工作了,我们编写一个抛物线的动画特效:
先编写一个PointFEvaluator自定义TypeEvaluator类:
public class PointFEvaluator implements TypeEvaluator<PointF> {
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
PointF pointF=new PointF();
pointF.x=200 * fraction * 3;
pointF.y=0.5f * 200 * (fraction * 3) * (fraction * 3);
return pointF;
}
}
evaluate()方法中,x轴以x=常量*变量因子改变,y轴以y=常量*变量因子的平方计算,这是抛物线的物理公式。
然后为ValueAnimator设置自定义的TypeEvaluator,并在updateListener监听器中设置属性值。
ValueAnimator().apply {
//设置动画持续时间
duration = 4000
//设置目标值
setObjectValues(PointF(0f,0f))
//设置自定义的 TypeEvalutor
setEvaluator(PointFEvaluator())
//设置加速插值器
interpolator = LinearInterpolator()
addUpdateListener { updateAnimation ->
//改变目标的属性值
val pointF=updateAnimation.animatedValue as PointF
iv_image.x=pointF.x
iv_image.y=pointF.y
}
start()
}
Google官网-属性动画
鸿洋-Android 属性动画(Property Animation)