参考:
- http://blog.csdn.net/harvic880925/article/details/50525521
- http://blog.csdn.net/harvic880925/article/details/50546884
属性动画(Property Animator)从Android3.0的版本开始支持,属性动画就是通过连续改变一个对象的属性,然后不停的绘制,从而实现动画;可实现View动画做不到的事情,比如改变一个控件的颜色;
属性动画包括:Value Animator 与 Object Animator
与View动画区别:
- 包不一样:
- View (android.view.animation)
- Property(android.animation)
- 名称不一致:
- View(XXXXAnimation,如:ScaleAnimation)
- Property(XXXXAnimator,如:ValueAnimator)
Value Animator
针对值
的改变,如:对一个数字从0到400 的变化过程,通过监听函数,来捕捉数字变化过程中的值;
btn_prop_start.onClick {
ValueAnimator.ofInt(0, 400).apply {
duration = 1000 // 持续时间
addUpdateListener { it ->
d("animValue: ${it.animatedValue} , fraction: ${it.animatedFraction}")
}
}.start()
}
- animatedValue: 表示 0 到 400的值
- animatedFraction:表示分量,取值[0,1] ,1表示动画完成
常用的API
- ofInt(int... values):
- ofFloat(float... values)
- setDuration(long value):
- getAnimatedValue():如上
- setRepeatMode(): 循环模式,
需setRepeatCount一起需要,单独设置无效
- setRepeatCount(): 循环次数
- cancel(): 动画取消
-ofObject()
2个监听器
- AnimatorUpdateListener: 如上
- Animator.AnimatorListener:监听动画状态(开始、结束、重复、取消)
valueAnim = ValueAnimator.ofInt(0, 200, 0).apply {
duration = 800 // 持续时间
// 监听器1
addUpdateListener { it ->
....
}
// 监听器2,监听动画状态
addListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {
}
override fun onAnimationEnd(animation: Animator?) {
}
override fun onAnimationCancel(animation: Animator?) {
}
override fun onAnimationStart(animation: Animator?) {
}
})
}
valueAnim.start()
设置插值器
插值器就是用来控制动画区间的值被如何计算出来的。比如LinearInterpolator插值器就是匀速返回区间点的值;
博客讲的很好
http://blog.csdn.net/harvic880925/article/details/50546884
Evaluator
动画监听器,拿到的是具体数值,那么这个具体数值是如何来的呢?这是通过Evaluator
将从插值器中获取的进度,转换成对应的具体数值;
Evaluator是一个转换器,他能把小数进度转换成对应的数值位置;
ofInt
时对应 IntEvaluator
, ofFloat
则对应FloatEvaluator
ofInt和ofFloat都是系统直接提供的函数,所以在使用时都会有默认的加速器和Evaluator来使用的,不指定则使用默认的;
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
类似于插值器,也可以自定义自己的Evaluator
ArgbEvalutor
官方提供的,用来实现颜色的过渡转换的;转换源码如下:
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
// 获取 A,R,G,B的初始值
float startA = ((startInt >> 24) & 0xff) / 255.0f;
float startR = ((startInt >> 16) & 0xff) / 255.0f;
float startG = ((startInt >> 8) & 0xff) / 255.0f;
float startB = ( startInt & 0xff) / 255.0f;
// 结束的
int endInt = (Integer) endValue;
float endA = ((endInt >> 24) & 0xff) / 255.0f;
float endR = ((endInt >> 16) & 0xff) / 255.0f;
float endG = ((endInt >> 8) & 0xff) / 255.0f;
float endB = ( endInt & 0xff) / 255.0f;
// convert from sRGB to linear
startR = (float) Math.pow(startR, 2.2);
startG = (float) Math.pow(startG, 2.2);
startB = (float) Math.pow(startB, 2.2);
endR = (float) Math.pow(endR, 2.2);
endG = (float) Math.pow(endG, 2.2);
endB = (float) Math.pow(endB, 2.2);
// compute the interpolated color in linear space
float a = startA + fraction * (endA - startA);
float r = startR + fraction * (endR - startR);
float g = startG + fraction * (endG - startG);
float b = startB + fraction * (endB - startB);
// convert back to sRGB in the [0..255] range
a = a * 255.0f;
r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
一个颜色的值由4部分组成:
初始值 | A | R | G | B |
---|---|---|---|---|
0xffff01 | ff | ff | 01 | 00 |
ofObject
ofInt 只能传int,ofFloat对应float,如需操作其他数据类型,可使用ofObject;
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {...}
例子1:使用ofObject 实现字母递增
// 1. 自定义TypeEvaluator
private inner class CharEvaluator : TypeEvaluator<Char> {
override fun evaluate(fraction: Float, startValue: Char, endValue: Char): Char {
val startInt = startValue.toInt()
val endInt = endValue.toInt()
val curInt = startInt + (endInt - startInt) * fraction
return curInt.toChar()
}
}
// 2,调用处,使用 CharEvaluator
btn_custom_evaluator.setOnClickListener (
{
// 传递 CharEvaluator
ValueAnimator.ofObject(CharEvaluator(), 'A', 'Z').apply {
duration = 5000
addUpdateListener { it ->
e(it.animatedValue.toString())
btn_custom_evaluator.text = it.animatedValue.toString()
}
interpolator = AccelerateInterpolator() as TimeInterpolator
}.start()
}
)
ObjectAnimator
ValueAnimator, 需要对其进行监听来获取数值的改变,这样来操作就相对麻烦了一些,ObjectAnimator继承自ValueAnimator,将动画过程,从监听动画过程中解放出来;
ObjectAnimator.ofFloat(btn_start1, "alpha", 1f, 0f, 1f).setDuration(800).start() // 没有创建动画监听器
各参数:
- 参数1:指定的动画对象,可以是一个控件;
- 参数2:对象的set方法去掉set部分;比如textview 的 alpha属性;
- 参数3:跟ValueAnimator可变参数类似了,表示属性值的变化值;
如果要实现其他动画(如:位移,旋转等),修改第2个参数,并配置好可变参数就行:
//ObjectAnimator.ofFloat(btn_start1, "alpha", 1f, 0f, 1f).setDuration(800).start()
ObjectAnimator.ofFloat(btn_start1, "y", 0f, 500f, 0f).setDuration(800).start()
// 其他 旋转等
ObjectAnimator.ofFloat(btn_start1, "rotation", 0f, 180f,0f).setDuration(1000).start()
这里的 alpha
,y
, rotation
这些属性哪里来的呢?我们的控件中,没有 rotation
这个属性; 原理是,内部不断调用setXXX
方法来实现动画的;
注意:如果对象(控件)没有对应的
setXXX
方法,那此动画将无效;
rotation 旋转属性
- rotation 以控件中心点围绕Z轴转;
- rotationX 以控件中心点围绕X轴转;
- rotationY 以控件中心点围绕Y轴转;
Z轴可以想象手机屏幕上钉了个钉子,围绕着钉子转,也就是z轴;
X上下翻动,翻日历;Y左右翻动,翻书;Z旋转,在屏幕拧螺丝
setTranslationX与setTranslationY 平移属性
- setTranslationX(translateX) :在X轴上的平移距离,以当前控件为原点,右为正方向,参数translationX表示移动的距离;
- setTranslationY(translationY) : 类似 ;
ObjectAnimator.ofFloat(text, "translationX", 0f, 200f, -200f, 0f).setDuration(800).start()
setScaleX与setScaleY 缩放
- setScaleX(scaleX):在X轴上缩放,scaleX表示缩放倍数
- setScaleY(scaleY):在Y轴上缩放
ObjectAnimator动画原理
图片来自原博客其他如:Evaluator 都与ValueAnimator一致;
PropertyValuesHolder
PropertyValuesHolder保存了动画过程中所需要操作的属性和对应的值。ofFloat()的内部实现其实就是将传进来的参数封装成PropertyValuesHolder实例来保存动画状态。在封装成PropertyValuesHolder实例以后,后期的各种操作也是以PropertyValuesHolder为主。
ObjectAnimator提供了ObjectAnimator.ofPropertyValuesHolder(Object target, PropertyValuesHolder... values)
来构造动画;
PropertyValuesHolder创建实例的函数:
public static PropertyValuesHolder ofFloat(String propertyName, float... values)
public static PropertyValuesHolder ofInt(String propertyName, int... values)
public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,Object... values)
public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)
ofFloat、ofInt方法对应的参数:
- propertyName : ObjectAnimator需要操作的属性名,即ObjectAnimator需要通过反射查找对应属性的setProperty()函数的那个property.
- values: 属性值,可多个;
对比下ObjectAnimator
的ofInt、ofFloat方法:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
PropertyValuesHolder 之 ofFloat、ofInt方法
创建2个PropertyValuesHolder对象,组合动画
val rotationHolder = PropertyValuesHolder.ofFloat("Rotation",
60f, -60f, 40f, -40f, -20f, 20f, 10f, -10f, 0f)
val colorHolder = PropertyValuesHolder.ofInt("BackgroundColor",
-0x1, -0xff01, -0x100, -0x1)
ObjectAnimator.ofPropertyValuesHolder(it, rotationHolder, rotationHolder, colorHolder).apply {
duration = 3000
}.start()
PropertyValuesHolder 之 ofObject方法
类似ObjectAnimator
的ofObject方法;源博客写的很精彩,请移步;
http://blog.csdn.net/harvic880925/article/details/50752838
我遇到的问题,实在很奇怪,点击开始没反应,但Evaluator会执行,代码如下:
btn_property_values_1.setOnClickListener {
// button是有setText的啊,奇怪,无效
val propertyValuesHolder = PropertyValuesHolder.ofObject("text", object : TypeEvaluator<String> {
override fun evaluate(fraction: Float, startValue: String, endValue: String): String {
val char = (startValue[0].toInt() + fraction * (endValue[0].toInt() -
startValue[0].toInt()))
return char.toString()
}
}, "A", "Z")
ObjectAnimator.ofPropertyValuesHolder(btn_property_values_1, propertyValuesHolder).apply {
duration = 3000
}.start()
}
Keyframe
如果要控制动画速率的变化,我们可以通过自定义插值器,也可以通过自定义Evaluator来实现。但复杂的插值器与Evaluator这个玩意需要数学基础的;
为了解决方便的控制动画速率的问题,谷歌定义了一个KeyFrame的类,即关键帧。
关键帧来自于动画,开始的时候定义一个,比如(0,0),结束的时候再定一个如:(300,300)然后在规定的时间内从开始到结束平滑的过渡过来,形成动画;
关键帧必须的2个元素:时间点、位置,表示意思为:时间点到了,物体的位置;
KeyFrame的生成方式
Keyframe kf0 = Keyframe.ofFloat(0, 0); // 进度为0时,动画数值为0
Keyframe kf1 = Keyframe.ofFloat(0.1f, -20f); // 进度为0.1f时,动画数值为-20
Keyframe kf2 = Keyframe.ofFloat(1f, 0);
Keyframe.ofInt(0.5f,2)
//方法签名
public static Keyframe ofInt(float fraction, int value)
参数说明:
- fraction:分量,进度,也就是从getInterpolation()返回的值,取值[0,1]
- value:当前进度对应的值
PropertyValuesHolder之ofKeyframe方法
// 方法签名
public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)
参数说明:
- propertyName:动画所要操作的属性名
- values:Keyframe的列表,PropertyValuesHolder会根据每个Keyframe的设定,定时将指定的值输出给动画。
使用keyframe实现动画步骤:
- 创建keyframe对象,可多个;
- 利用PropertyValuesHolder.ofKeyframe()方法来生成PropertyValuesHolder对象;
- ObjectAnimator.ofPropertyValuesHolder()生成对应的Animator
例子:
val frame0 = Keyframe.ofFloat(0f, 0f)
val frame1 = Keyframe.ofFloat(0.1f, -20f)
val frame2 = Keyframe.ofFloat(1f, 0f)
val propertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation", frame0, frame1, frame2)
ObjectAnimator.ofPropertyValuesHolder(it, propertyValuesHolder).apply {
duration = 1000
}.start()
Keyframe常用函数
生成函数:
/**
* ofFloat
*/
public static Keyframe ofFloat(float fraction) // 当前关键帧所在的动画进度位置
public static Keyframe ofFloat(float fraction, float value)
/**
* ofInt
*/
public static Keyframe ofInt(float fraction) // 当前关键帧所在的动画进度位置
public static Keyframe ofInt(float fraction, int value)
/**
* ofObject
*/
public static Keyframe ofObject(float fraction)
public static Keyframe ofObject(float fraction, Object value)
常用函数:
比如通过上的生成函数,设置了动画进度,但如要设置值,就需要用到下面的了
/**
* 设置fraction参数,即Keyframe所对应的进度
*/
public void setFraction(float fraction)
/**
* 设置当前Keyframe所对应的值
*/
public void setValue(Object value)
/**
* 设置Keyframe动作期间所对应的插值器
*/
public void setInterpolator(TimeInterpolator interpolator)
对于设置插值器函数setInterpolator
表示从上一个Keyframe开始到当前设置插值器的Keyframe时,这个过程值的计算是利用这个插值器的;
val frame0 = Keyframe.ofFloat(0f, 0f)
val frame1 = Keyframe.ofFloat(0.1f, -20f)
// 从frame0到frame1使用bounce
frame1.interpolator = BounceInterpolator() as TimeInterpolator
val frame2 = Keyframe.ofFloat(0.2f, 20f)
// 从frame1到frame2使用Linear
frame2.interpolator = LinearInterpolator() as TimeInterpolator
如果不设置插值器,默认使用Linear
Keyframe 之 ofObject
ofObject
必须要对应Evaluator
来获取动画值
val frame0 = Keyframe.ofObject(0.0f, 'A')
val frame1 = Keyframe.ofObject(0.3f, 'F')
val frame2 = Keyframe.ofObject(1.0f, 'Z')
// 必要的Evaluator
val propertyValuesHolder = PropertyValuesHolder.ofKeyframe("CharText", frame0, frame1, frame2).apply {
setEvaluator(object : TypeEvaluator<Char> {
override fun evaluate(fraction: Float, startValue: Char, endValue: Char): Char {
return (startValue.toInt() + fraction * (endValue.toInt() - startValue.toInt())).toChar()
}
})
}
ObjectAnimator.ofPropertyValuesHolder(text, propertyValuesHolder).apply {
duration = 2000
}.start()
有意思的尝试
去掉某些关键帧,如上,关键帧0(frame0 ),最后一帧(frame2)。分享的时候尝试;
多个动画同时播放
// 抖動
val frame0 = Keyframe.ofFloat(0f, 0f)
val frame1 = Keyframe.ofFloat(0.1f, -20f)
val frame10 = Keyframe.ofFloat(1f, 0f)
val propertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation", frame0, frame1, frame2
, frame7, frame8, frame9, frame10)
// 縮放X
val scaleXFrame0 = Keyframe.ofFloat(0f, 0f)
val scaleXFrame1 = Keyframe.ofFloat(.1f, 1.1f)
val scaleXFrame6 = Keyframe.ofFloat(1f, 1.0f)
val scaleXProperty = PropertyValuesHolder.ofKeyframe("scaleX", scaleXFrame0, scaleXFrame1, scaleXFrame2
, scaleXFrame3, scaleXFrame4, scaleXFrame5, scaleXFrame6)
// 縮放Y
val scaleYFrame0 = Keyframe.ofFloat(0f, 0f)
val scaleYFrame1 = Keyframe.ofFloat(.1f, 1.1f)
val scaleYFrame6 = Keyframe.ofFloat(1f, 1.0f)
val scaleYProperty = PropertyValuesHolder.ofKeyframe("scaleY", scaleYFrame0, scaleYFrame1, scaleYFrame2
, scaleYFrame3, scaleYFrame4, scaleYFrame5, scaleYFrame6)
// 組合
ObjectAnimator.ofPropertyValuesHolder(image_view, propertyValuesHolder, scaleXProperty, scaleYProperty).apply {
duration = 1000
}.start()
如上,省略了部分重复代码,借助Keyframe,不需要使用AnimatorSet,也能实现多个动画同时播放。这也是ObjectAnimator中唯一一个能实现多动画同时播放的方法,其它的ObjectAnimator.ofInt,ObjectAnimator.ofFloat,ObjectAnimator.ofObject都只能实现针对一个属性动画的操作!
网友评论