美文网首页
Property Animator 属性动画基础

Property Animator 属性动画基础

作者: zhaoyubetter | 来源:发表于2018-03-19 23:05 被阅读17次

    参考:

    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()
    }
    
    • 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()
    

    这里的 alphay, 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实现动画步骤:

    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都只能实现针对一个属性动画的操作!

    相关文章

      网友评论

          本文标题:Property Animator 属性动画基础

          本文链接:https://www.haomeiwen.com/subject/sexyqftx.html