简述
在上篇文章中,我们介绍了Android中的帧动画和补间动画,本篇内容则将会对Android中另一种动画——属性动画进行简单介绍。
属性动画的功能非常强大,可以满足我们平常对动画的开发。
系列文章:
Android动画学习(一):帧动画和补间动画
Android动画学习(二):基本属性动画
Android动画学习(三):自定义属性动画
和补间动画的区别
上篇内容中,我们讲述了补间动画的简单实现,补间动画主要能够对View实现如下几种操作:
- 平移
- 旋转
- 缩放
- 淡入淡出效果
但是补间动画也就只能实现上述4种操作或者是几种操作的混合内容,不能够实现一些其他的动画操作:例如对view的背景颜色进行动画变化。而相对更加灵活的属性动画则可以实现上述功能。
除此之外,补间动画和属性动画还有一个重要的区别:
补间动画只会改变View的显示效果,不会去改变View的真正属性;而属性动画则会确实的改变View的属性。
举例来说,当前实现一个平移动画使View1从位置A移动到B。
如果采用补间动画,我们可以看到视觉效果上View1从A移动到了B,当时当你点击在B处的View1时却不会触发View1的点击事件,点击事件的触发仍然是在A处触发的。
如果采用属性动画,则View1的属性会直接变动,包括其点击事件都会在B处触发。
上述就是对于属性动画的基本介绍了,接下来我会介绍一下属性动画的简单使用。
属性动画
1. 属性动画参数
动画原理
在介绍属性动画使用之前,我们先来分析一下动画特性和原理进行分析。
动画的工作原理是不断的去刷新视图,当以特定的高帧频率刷新视图时,在视觉上就会连续效果,和电影原理是一样的。
属性动画同样如此,如下图,实现一个View的X轴平移动作(线性插值):
可以看到是视图以每10ms就移动10px(线性插值,动画速度恒定),视图以每10ms就刷新一次。
动画属性
从上面的实例中,我们可以总结下定义一个动画所需要的基本属性:
- 动画时长:动画的播放时长
- 动画对象:动画应用的对象View
- 动画播放逻辑:淡入淡出,缩放,平移等动画的实际播放动作,包括动画播放的先后顺序
- 重复和计数:动画是否能够重复,重复次数。
- 时间插值:动画播放快慢频率控制。
属性动画核心类
属性动画同样需要这几个条件,主要涉及的核心类包括如下三个:
类名 | 说明 |
---|---|
ValueAnimator | 属性动画核心类能够实现属性动画的核心功能,也计算添加动画效果属性的值 |
ObjectAnimator | ValueAnimator子类,用于实现对指定view的动画 |
AnimatorSet | 实现组合动画的类 |
2. ValueAnimator
ValueAnimator是属性动画中的核心类,它继承自Animator类,类图如下(截自官方文档):
类图上可以看到ValueAnimator的几个核心属性和方法:
属性或方法 | 名称 |
---|---|
TimeInterpolator | 动画插值 |
TypeEvaluator | 动画评估,指定动画的变化 |
duration | 动画时长 |
start() | 播放动画 |
startPropertyValue | 起始值 |
endPropertyVaule | 结束值 |
TimeInterpolator
动画插值,决定动画执行的快慢。其中Android SDK中提供了一些常见的插值器,在android.view.animation
中,具体内容如下:
类 | 说明 |
---|---|
AccelerateDecelerateInterpolator |
该插值器的变化率在开始和结束时缓慢但在中间会加快。 |
AccelerateInterpolator |
该插值器的变化率在开始时较为缓慢,然后会加快。 |
AnticipateInterpolator |
该插值器先反向变化,然后再急速正向变化。 |
AnticipateOvershootInterpolator |
该插值器先反向变化,再急速正向变化,然后超过定位值,最后返回到最终值。 |
BounceInterpolator |
该插值器的变化会跳过结尾处。 |
CycleInterpolator |
该插值器的动画会在指定数量的周期内重复。 |
DecelerateInterpolator |
该插值器的变化率开始很快,然后减速。 |
LinearInterpolator |
该插值器的变化率恒定不变。 |
OvershootInterpolator |
该插值器会急速正向变化,再超出最终值,然后返回。 |
TypeEvalutaor
TypeEvalutaor指定动画的变化内容,常见的内容有如下几种:
类/接口 | 说明 |
---|---|
IntEvalutor | 计算Int的值变化 |
FloatEvaluator | 计算Float属性的值变化 |
ArgbEvaluator | 计算颜色的值变化 |
TypeEvaluator | 动画变化,可以实现此接口自定义内容 |
ValueAnimator使用
在ValueAnimator内部使用了一种时间循环机制来计算值与值的动画过度,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就可以完成从初始值平滑的过度到结束值这样的效果。
ValueAnimator的使用并不复杂,截取官方文档上的示例,实现一个在1000ms内从0过度到100的一个数值动画,可以如下代码实现:
ValueAnimator.ofFloat(0f, 100f).apply {
duration = 1000
start()
}
但是在在执行了上述代码后,却没有看到显示效果,原因是由于这是一个数值动画,不涉及到View的变化,因此如果要获取到动画运行的状态,必须要借助监听器AnimatorUpdateListener
来实现,如下:
ValueAnimator.ofObject(...).apply {
...
addUpdateListener { updatedAnimation ->
// You can use the animated value in a property that uses the
// same type as the animation. In this case, you can use the
// float value in the translationX property.
textView.translationX = updatedAnimation.animatedValue as Float
}
...
}
对于
ofObject
方法需要注意的是此方法的参数可以是多个,我们可以通过设置多个参数实现多个动画节点,如ValueAnimator.ofFloat(0f, 100f,20f,90f),则就会实现从0到100,再到20,最后到90的动画效果。
3. ObjectAnimator
相对于ValueAnimator
实现数值的动画过度,能实现View动画的ObjectAnimator
类应该我们经常使用到属性动画类。但是实际上ObjectAnimator
是ValueAnimator
的一个子类,其动画生成机制是一样的。
其使用也是比较简单的,示例如下,实现一个文本控件平移动画:
ObjectAnimator.ofFloat(textView, "translationX", 100f).apply {
duration = 1000
start()
}
同样的,我们也能够实现透明度,旋转,缩放的动画效果,如下:
/**
* 透明度属性动画
*/
private fun setAlphaProAnimation(v: View?) {
if (v != null) {
val anim = ObjectAnimator.ofFloat(v, "alpha", 0f, 1f, 0f, 1f)
anim.duration = 3000
anim.start()
}
}
/**
* 缩放属性动画
*/
private fun setScaleProAnimation(v: View?) {
if (v != null) {
val anim = ValueAnimator.ofFloat(0f, 1f)
anim.duration = 3000
anim.addUpdateListener {
val value = it.animatedValue
v.scaleX = value as Float
v.scaleY = value as Float
}
anim.start()
}
}
/**
* 平移属性动画
*/
private fun setTranProAnimation(v: View?) {
if (v != null) {
val anim = ObjectAnimator.ofFloat(v, "translationX", 0f, 100f)
anim.duration = 1000
anim.start()
}
}
/**
* 旋转属性动画
*/
private fun setRotationProAnimation(v: View?) {
if (v != null) {
val anim = ObjectAnimator.ofFloat(v, "rotation", 0f, 90f)
anim.duration = 1000
anim.start()
}
}
看到这里,大家可以比较疑惑这个属性的值是从怎么地方获取的,如rotation
、alpha
这些值,实际上这些值是在View的属性里去获取到的,去查询到当前属性名的get
和set
方法,从View的代码中可以看到这些常见的属性名称:
stream.addProperty("drawing:alpha", getAlpha());
相应的,其他常见属性也是如此查询到的。
那么为什么可以通过直接设置View属性就可以设置ObjectAnimator动画呢?
究其原因可以查看源码,其实ObjectAnimator
的动画原理是不断的对View对象里的属性进行值的重置得到的,而像alpha
这些属性都是View的基本属性,所以直接填写就能够直接更新属性,如果需要设置其他属性变化就需要重新自定义当前View的属性。
4. AnimatorSet组合动画
独立的动画实现的效果是比较有限的,因此在平常的开发过程中将多个动画组合到一起播放是经常碰到的问题。Android SDK中自然也是有方法能够实现组合动画:AnimatorSet
。
AnimatoeSet
类提供了一个play()
方法用来播放动画,同时也提供了用来对动画顺序进行管控的方法:
- after(Animator anim):将现有动画anim插入到传入的动画之后执行
- before:将现有的动画插入到传入的动画之前执行
- with:将现有的动画和传入的动画同时执行
- after(long time):将 现有的动画延迟指定的时间后执行,单位ms
示例如下:
val bouncer = AnimatorSet().apply {
play(bounceAnim).before(squashAnim1)
play(squashAnim1).with(squashAnim2)
play(squashAnim1).with(stretchAnim1)
play(squashAnim1).with(stretchAnim2)
play(bounceBackAnim).after(stretchAnim2)
}
val fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
duration = 250
}
AnimatorSet().apply {
play(bouncer).before(fadeAnim)
start()
}
上述示例中的动画执行顺序如下:
- 播放动画
bounceAnim
。 - 同时播放动画
squashAnim1
,squashAnim2
,stretchAnim1
,stretchAnim2
。 - 播放
bounceBackAnim
. - 播放
fadeAnim
.
5. 动画监听器
可以使用下述监听器来监听动画播放期间的重要事件。
-
Animator.AnimatorListener
-
onAnimationStart()
- 在动画开始播放时调用。 -
onAnimationEnd()
- 在动画结束播放时调用。 -
onAnimationRepeat()
- 在动画重复播放时调用。 -
onAnimationCancel()
- 在动画取消播放时调用。取消的动画也会调用 -
onAnimationEnd()
,动画结束调用。
-
-
ValueAnimator.AnimatorUpdateListener
-
onAnimationUpdate()
- 对动画的每一帧调用。
-
总结
本文对属性动画的概念和属性进行了简单的说明,可以实现和简单的平移、旋转、缩放和透明度控制的简单组合动画,自定义动画后续会进行探讨学习。