Property Animator 属性动画基础

2018-03-19  本文已影响17人  zhaoyubetter

参考:

  1. http://blog.csdn.net/harvic880925/article/details/50525521
  2. http://blog.csdn.net/harvic880925/article/details/50546884

属性动画(Property Animator)从Android3.0的版本开始支持,属性动画就是通过连续改变一个对象的属性,然后不停的绘制,从而实现动画;可实现View动画做不到的事情,比如改变一个控件的颜色;

属性动画包括:Value Animator 与 Object Animator

与View动画区别:

  1. 包不一样:
    • View (android.view.animation)
    • Property(android.animation)
  2. 名称不一致:
    • 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()
}

常用的API

2个监听器

  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()  // 没有创建动画监听器

各参数:

如果要实现其他动画(如:位移,旋转等),修改第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()

这里的 alphay, rotation 这些属性哪里来的呢?我们的控件中,没有 rotation这个属性; 原理是,内部不断调用setXXX方法来实现动画的;

注意:如果对象(控件)没有对应的setXXX方法,那此动画将无效;

rotation 旋转属性

摘自源博客的图片(绿色为手机屏幕)

Z轴可以想象手机屏幕上钉了个钉子,围绕着钉子转,也就是z轴;
X上下翻动,翻日历;Y左右翻动,翻书;Z旋转,在屏幕拧螺丝

setTranslationX与setTranslationY 平移属性

ObjectAnimator.ofFloat(text, "translationX", 0f, 200f, -200f, 0f).setDuration(800).start()

setScaleX与setScaleY 缩放

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方法对应的参数:

对比下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)

参数说明:

PropertyValuesHolder之ofKeyframe方法

// 方法签名
public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)

参数说明:

使用keyframe实现动画步骤:

  1. 创建keyframe对象,可多个;
  2. 利用PropertyValuesHolder.ofKeyframe()方法来生成PropertyValuesHolder对象;
  3. 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都只能实现针对一个属性动画的操作!

上一篇 下一篇

猜你喜欢

热点阅读