Android-属性动画
学习自
《Android开发艺术探索》
《属性动画相关的官方文档》
属性动画漫谈
Android的属性动画是Android3.0 推出的,属性动画完全改善了View动画的弊端,如果不是一些地方还会用到View动画属性动画早就将View动画完全替代了。属性动画之所以被称之为属性动画,是因为属性动画通过动态地改变View的属性来实现动画效果。比如说,属性动画通过改变一个View的X轴坐标来实现移动View位置的动画,通过属性动画移动了View的位置是真正意义上的改变了View的位置,而不是仅仅是像View动画那样将View的图像显示在指定位置。
属性动画框架主要结构
- Animator,所有属性动画的父类,为开始、结束属性动画和添加动画监听器提供了支持。
- AnimatorSet,属性动画的合集
- ValueAnimator, 仅仅是对数值的属性动画,并不能表现在UI上
- ObjectAnimator,对View的属性动画
ValueAnimator
//设置ValueAnimator,1-100的浮点数之间的动画渐变
val valueAnimator = ValueAnimator.ofFloat(1.0F, 100.0F).setDuration(1000)
//设置一个监听器
valueAnimator.addUpdateListener {
val value = it.animatedValue as Float
Log.e("TAG", "Value=$value")
}
valueAnimator.start()
//输出结果
07-23 15:46:00.944 13240-13240/top.littledavid.studyanimation E/TAG: Value=1.0
07-23 15:46:00.945 13240-13240/top.littledavid.studyanimation E/TAG: Value=1.0
07-23 15:46:00.978 13240-13240/top.littledavid.studyanimation E/TAG: Value=1.0705773
07-23 15:46:01.010 13240-13240/top.littledavid.studyanimation E/TAG: Value=1.609426
//............................................
07-23 15:46:01.431 13240-13240/top.littledavid.studyanimation E/TAG: Value=47.857613
07-23 15:46:01.450 13240-13240/top.littledavid.studyanimation E/TAG: Value=50.5
07-23 15:46:01.465 13240-13240/top.littledavid.studyanimation E/TAG: Value=53.142387
07-23 15:46:01.484 13240-13240/top.littledavid.studyanimation E/TAG: Value=55.6226
07-23 15:46:01.499 13240-13240/top.littledavid.studyanimation E/TAG: Value=58.243496
//...........................................
07-23 15:46:01.915 13240-13240/top.littledavid.studyanimation E/TAG: Value=99.73422
07-23 15:46:01.931 13240-13240/top.littledavid.studyanimation E/TAG: Value=99.92943
07-23 15:46:01.948 13240-13240/top.littledavid.studyanimation E/TAG: Value=100.0
上面的代码是一个ValueAnimator的Demo,ValueAnimator是仅仅是一种对数值的动画,ofFloat()
方法支持任意参数,如果指定了那么就会在规定时间内,完成数值的渐变。输出结果上我们可以看出来,ValueAnimator平滑的从1过渡到了100。这里的数值的变化是平滑线性的,如果你不想要这种效果,那么可以使用 差值器
来加速或者减速。关于差值器我们会在另一章提到。
因为ValueAnimator不能作用于对象,仅仅是对数值做了一个动画,如果我们想将ValueAnimator作为动画的话,需要在ValueAnimator的动画监听中来手动地更新对象。
/**
建立一个简单的Person类,接线来我们将会通过ValueAnimator来更新此对象的Age
*/
class Person(var name: String, var age: Int) {
override fun toString(): String {
return "I am is $name; I am $age years old!"
}
}
var person = Person("鲁迅认识的那只猹", 0)
ValueAnimator.ofInt(1, 50).apply {
duration = 3000
//动画的每一帧都会调用此监听的回调
//我们可以在此监听中更新我们的对象
addUpdateListener {
person.age = it.animatedValue as Int
Log.e("TAG", person.toString())
}
start()
}
/**
07-23 16:11:28.331 14623-14623/top.littledavid.studyanimation E/TAG:
-------------------------------------
07-23 16:11:28.882 14623-14623/top.littledavid.studyanimation E/TAG:
-------------------------------------
07-23 16:11:31.332 14623-14623/top.littledavid.studyanimation E/TAG:
*/
ObjectAnimator
下面是一个通过ObjectAnimator来更改View的透明度的动画
/**
参数解释
1. 动画作用的对象
2. 要更改的属性
3. 设置动画渐变的数值
*/
ObjectAnimator.ofInt(dogIV, "alpha", 0, 255).setDuration(3000).start()
ObjectAnimator的常用属性和方法:
- propertyName 属性动画作用对象的属性的名称
- duration 属性动画持续的时间
- valueFrom 属性的
- values 属性动画的值
- startDelay 动画延迟,当调用start方法后,多少毫秒后开始动画
- repeatCount 属性动画重复的次数 ObjectAnimator.INFINITE(-1) 表示无线循环
- repeatMode 属性动画重复的方式为
ObjectAnimator.RESTART
和ObjectAnimator.REVERSE
Restart表示重新开始,Reverse表示动画翻转交替进行。 - ObjectAnimator.of
Type指的是数据类型,比如说Int Float等
属性动画也可以通过XMl文件来定义,需要放在 res/animator
目录下:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="alpha"
android:repeatCount="0"
android:repeatMode="restart"
android:valueFrom="0"
android:valueTo="255"
android:valueType="intType" />
代码调用
var animator = AnimatorInflater.loadAnimator(this, R.animator.alpa_animator)
animator.setTarget(dogIV)
animator.start()
对任意属性做动画
通过上面的Demo的学习,能够得知属性动画的原理如下:
属性动画要求其作用的对象的必须为该属性提供get和set方法,属性动画通过外界传递的该属性的初始值和最终值,以动画的形式来不断地调用 set
方法赋值,随着时间的推移,随着时间的推移该值会越来越接近最终值,如果想要动画生效需要满足一下的条件:
- 对象必须提供了set<属性> 方法,如果动画没有提供初始值,那么属性动画还会去调用get方法来获取属性的值,作为初始值,如果不满足条件则Crash。
- set方法对属性作用的改变必须能够以某种形式反映出来,比如说UI改变,否则,动画不生效但是不会Crash。
那么现在有一个问题
通过属性动画在3秒内将Button的长度从50px改到500px.
我们都知道Button的是没有提供setWidth方法的。所以我们不能直接设置width,所以我们需要对Button做一层包装,通过一个包装类来包装一下Button对象并且提供get/setWidth 方法。
fun startAlphaObjectAnimator(view: View) {
ObjectAnimator.ofInt(ButtonWrapper(testBtn), "width", 50, 500)
.setDuration(3000)
.start()
}
//Kotlin的写法要比Java的简化,直接通过定义属性来解决
private class ButtonWrapper(var targetBtn: Button) {
var width = targetBtn.width
set(value) {
targetBtn.layoutParams.width = value
}
}
在上面的代码中我们为Button类做了一些封装提供了get和set方法,因为Button不能够直接设置宽度,这是一种很好的解决思路。对一个对象的不能直接设置属性动画的属性来设置属性动画有一下集中解决办法:
- 如果能够修改类,那么提供上 get 和 set
- 通过包装原有的类的对象,在包装类中提供get和set方法
- 通过ValueAnimator来实现动画,在监听中更新属性