前言
属性动画是API 11 新加入的特性,和View 动画不同,它对作用对象进行了拓展,属性动画可以对任何对象做动画,甚至可以没有对象。 属性动画可以对任意对象的属性进行动画而不仅仅是View,动画默认时间间隔300ms,默认帧率10ms/帧。其可以达到的效果是:在一个时间间隔内完成对象从一个属性值到另一个属性值的改变。采用开源动画库nineoldandroids 兼容API 11 以前的系统。
比较常用的几个动画类是:ValueAnimator、ObjectAnimator、AnimatorSet,其中ObjectAnimator继承自ValueAnimator,AnimatorSet是动画集合,可以定义一组动画。
ObjectAnimator
ObjectAnimator是属性动画框架中最重要的实行类,创建一个ObjectAnimator只需通过他的静态工厂类直接返回一个ObjectAnimator对象。参数包括: 一个对象和对象的属性名字,但这个属性必须有get和set函数,内部会通过Java反射机制来调用set函数修改对象属性值。也可以调用setInterpolator 设置相应的差值器。
- 平移动画
val transObj=ObjectAnimator.ofFloat(btn,"translationY",btn.height.toFloat())
transObj.repeatCount=10
transObj.start()
效果:
- 透明度动画
val colorAnim=ObjectAnimator.ofInt(btn,"backgroundColor",0xFFFF8080.toInt(),0xFF8080FF.toInt())
colorAnim.duration=3000
colorAnim.repeatMode=ValueAnimator.REVERSE
colorAnim.repeatCount=ValueAnimator.INFINITE
colorAnim.setEvaluator(ArgbEvaluator())
colorAnim.start()
效果:
AnimatorSet
val set=AnimatorSet()
val colorAnim=ObjectAnimator.ofInt(btn,"backgroundColor",0xFFFF8080.toInt(),0xFF8080FF.toInt(),0xFFFF8080.toInt())
val rotateAnimatorX=ObjectAnimator.ofFloat(btn,"rotationX",0f,360f,0f)
val rotateAnimatorY=ObjectAnimator.ofFloat(btn,"rotationY",0f,180f,0f)
val translationAnimatorX=ObjectAnimator.ofFloat(btn,"translationX",0f,90f,0f)
val translationAnimatorY=ObjectAnimator.ofFloat(btn,"translationY",0f,90f,0f)
val scaleAnimatorX=ObjectAnimator.ofFloat(btn,"scaleX",1f,1.5f,1f)
val scaleAnimatorY=ObjectAnimator.ofFloat(btn,"scaleY",1f,0.5f,1f)
val alpha=ObjectAnimator.ofFloat(btn,"alpha",1f,0.25f,1f)
//使用playTogether
set.playTogether(rotateAnimatorX,rotateAnimatorY,translationAnimatorX,translationAnimatorY,scaleAnimatorX,scaleAnimatorY,alpha,colorAnim)
// 使用 after()、before()、with() 方法
set.play(colorAnim).with(rotateAnimatorY).with(translationAnimatorY).with(alpha).after(scaleAnimatorY)
set.duration=5000
set.reverse()
set.start()
效果 :
使用playTogether效果
使用 after()、before()、with() 方法效果:
方法
- playTogether(Animator… items)
set集合中各种动画效果同时执行 - after(Animator anim)
将现有动画插入到传入的动画之后执行 - after(long delay)
将现有动画延迟指定毫秒后执行 - before(Animator anim)
将现有动画插入到传入的动画之前执行 - with(Animator anim)
将现有动画和传入的动画同时执行
PropertyValuesHolder
在属性动画中,如果针对同一个对象的多个属性,要同时作用多种动画,可以使用PropertyValuesHolder来实现
val pvh1 = PropertyValuesHolder.ofFloat("translationX", 300f)
val pvh2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0.1f)
val pvh3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0.1f)
ObjectAnimator.ofPropertyValuesHolder(button, pvh1, pvh2, pvh3).setDuration(1000).start()
效果:
ValueAnimator
ValueAnimator 本身不作用于任何对象,也就是说直接使用它没有任何动画效果。它可以对一个值做动画,然后可以监听其动画过程,在动画过程中修改我们对象的属性值,这样也就相当于我们的对象做了动画。
数字滚动 例子:
private fun valueAnimEx(btn: Button){
val anim=ValueAnimator.ofFloat(0f, 100f)
anim.addUpdateListener { animator->
btn.text="¥${animator.animatedValue}"
}
anim.duration=3000
anim.interpolator=LinearInterpolator()
anim.start()
}
效果:
对任意属性做动画
属性动画动画的原理: 属性动画要求动画作用的对象提供该属性的get 和set 方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值不一样,确切来说,随着时间的推移,所传递的值越来越接近最终值。
情景: 给Button 加一个动画,让这个Button的宽度增加到指定的宽度
解决方法
- 给你的对象加上get 和 set方法,前提是有权限的话
- 用一个类来包装原始对象,间接为其提供set 和get方法
- 采用ValueAnimator,监听动画过程,自己实现属性的改变
第一种方案多数情况下不可行,除非前提是有权限的话,此种不再赘述。下面第二和第三方案
private fun performAnimate(target: Button,start :Int,end :Int){
// 采用ValueAnimator,监听动画过程,自己实现属性的改变
val valueAnimator=ValueAnimator.ofInt(1,100)
val mEvaluator=IntEvaluator()
valueAnimator.addUpdateListener {animator->
val fraction=animator.animatedFraction
target.layoutParams.width=mEvaluator.evaluate(fraction,start,end)
target.requestLayout()
}
valueAnimator.duration=5000
valueAnimator.start()
// 用一个类来包装原始对象,间接为其提供set 和get方法
val viewWrapper=ViewWrapper(target)
val objectAnimator=ObjectAnimator.ofInt(viewWrapper,"width",start,end)
objectAnimator.duration=5000
objectAnimator.start()
}
inner class ViewWrapper{
private var viewTarget: View?=null
constructor(viewTarget: View?) {
this.viewTarget = viewTarget
}
fun setWidth(width :Int){
viewTarget?.layoutParams?.width=width
viewTarget?.requestLayout()
}
fun getWidth():Int?{
return viewTarget?.layoutParams?.width
}
}
效果:
相关API
- 属性动画监听器
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}
- Xml 布局属性
// set标签 对应 AnimatorSet
<set xmlns:android="http://schemas.android.com/apk/res/android"
//together 表示动画集合中子动画同时播放
//sequentially 表示动画集合中子动画 按照前后顺序依次播放
android:ordering=["together"|"sequentially"]>
// animator标签 对应 ValueAnimator
<animator
// 表示动画时长
android:duration="int"
//表示属性动画作用对象的属性的名称
android:propertyName="string"
// 表示动画的重复 0默认值,-1逆向重复
android:repeatCount="int"
// 表示动画的重复模式 reverse逆向重复,restart连续重复
android:repeatMode=["reverse"|"restart"]
// 动画延时的时间
android:startOffset="int"
// 属性的起始值
android:valueFrom="float|color|int"
// 属性的结束值
android:valueTo="float|color|int"
// 表示android:propertyName所指定的属性的类型,如果所指定的属性值是颜色
// 那么不需要指android:valueType,系统会自动对颜色类型的属性做处理
android:valueType=["intType"|"floatType"]/>
// objectAnimator标签 对应 ObjectAnimator
<objectAnimator
android:duration="int"
android:propertyName="string"
android:repeatCount="int"
android:repeatMode=
android:startOffset="int"["reverse"|"restart"]
android:valueFrom="float|color|int"
android:valueTo="float|color|int"
android:valueType=["intType"|"floatType"] />
</set>