Android属性动画之ValueAnimator

一 前言

属性动画通过改变一个对象的属性值来进行动画,属性动画包含了以下几个特性: 
(1)持续时间(Duration) :主要用来定义动画的持续时间,默认值为300ms。

(2)时间插值器(Time interpolator): 指定时间变化的百分比,就是当前流逝时间除以指定的持续时间,这个可以自定义,继承Interpolator,重写getInterpolation方法。

(3)重复次数和行为(Repeat count and behavior) :指定动画的执行次数和动画的重复模式

(4)动画集(Animator sets) :可以把多个动画放到一个集合中,是他们同时执行,或者指定它们直接的顺序和延迟。

(5)Frame refresh delay(帧刷新延迟) :可以指定如何去刷新动画的帧,默认是每10ms刷新一次,这个刷新也取决于系统的繁忙程度。

     上面我们知道属性动画就是改变对象的属性值来实现动画,ValueAnimator的特点就是你不需要明确的指定你要改变的对象和属性,你只需要得到一个动态的值来自己去设置相应对象的属性,也就是它就是提供属性的变化值,你拿到这个值可以动态的更改对象属性值。总结一句就是监听动画过程,自己实现属性的改变转载请注明出处:小石头的博客  http://blog.csdn.net/lu1024188315/article/details/74518599

二 方法

1 获取ValueAnimator实例的函数

public static ValueAnimator ofInt(int... values) 

public static ValueAnimator ofFloat(float... values)

public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) 

public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values)

      从上面我们可以看到,不同的方式其实对应的就是不同类型的变化值:

第一种方式的变化值类型为Int;

第二种方式的变化值类型为Float

第三种方式的变化值类型为Object

第四种方式的变化值类型为PropertyValuesHolder,它其实是一个集合。

参数分析:
(1)TypeEvaluator类型估值器,见下文详解
(2) PropertyValuesHolder 用来保存属性信息和这个属性的值 ,见下文详解
(3)values:属性值变化范围

2 常有方法

public ObjectAnimator setDuration(long duration) 

设置持续时间 

public void setEvaluator(TypeEvaluator value) 

设置估值器 

public void setInterpolator(/*Time*/Interpolator value) 

设置插值器

public void setTarget(Object target) 

设置目标对象 

public void setRepeatCount(int value) 

设置动画重复次数 

public void setRepeatMode(int value) 

设置重复模式 

public void setValues(PropertyValuesHolder... values) 

设置值

public void setStartDelay(long startDelay) 

设置启动延时

public static void setFrameDelay(long frameDelay) 

设置帧延迟

public void setIntValues(int... values) 

设置Int值,对应ValueAnimator.ofInt函数

public void setFloatValues(float... values) 

设置Float值。对应ValueAnimator.ofFloat函数

public void setCurrentPlayTime(long playTime) 

设置当前执行时间

public void setObjectValues(Object... values) 

设置Object值,对应ValueAnimator.ofObject函数

三 属性值保存过程分析

先来个示例:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_animator_test);
        img = (ImageView) findViewById(R.id.img);
        ValueAnimator animator = ValueAnimator.ofFloat(0, 500);
        // 这里指定变化持续时间
        animator.setDuration(1000);
        //开始动画
        animator.start();
        //开始动画后,我们可以动态的获取变化值
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
            @Override
            public void onAnimationUpdate(ValueAnimator animation){
                //根据变化值来设置imageView对象的Y轴坐标,这样就实现了imageView的垂直移动
                img.setTranslationY((Float) animation.getAnimatedValue());
            }
        });

}

说明,在这里调用了ValueAnimator的静态函数ofFloat获取该对象实例,那么我们来看看这个函数:

ValueAnimator#ofFloat:

public static ValueAnimator ofFloat(float... values) {
        ValueAnimator anim = new ValueAnimator();//通过构造函数创建一个实例
        anim.setFloatValues(values);//设置这些浮点值
        return anim;
}

说明,这个方法主要两个作用:

(1)通过构造函数创建一个实例

(2)通过该实例调用setFloatValues函数设置值

ValueAnimator#构造函数:

public ValueAnimator() {
}

说明,很明显,构造函数很简单,啥事都没有干,接下来看setFloatValues:

ValueAnimator#setFloatValues:

PropertyValuesHolder[] mValues;
public void setFloatValues(int... values) {
        if (values == null || values.length == 0) {//values不为null不走这里
            return;
        }
        if (mValues == null || mValues.length == 0) {
            //第一到这里mValues为null,走这里,mValues 为PropertyValuesHolder类型,用来存在属性的值
            setValues(PropertyValuesHolder.ofFloat("", values));
        } else {
            PropertyValuesHolder valuesHolder = mValues[0];
            valuesHolder.setIntValues(values);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
}

说明,PropertyValuesHolder是什么鬼?来看下它的英文注释:

/**
* This class holds information about a property and the values that that property
* should take on during an animation. PropertyValuesHolder objects can be used to create
* animations with ValueAnimator or ObjectAnimator that operate on several different properties
* in parallel.
*/

大致明白了PropertyValuesHolder用来干嘛的了,它就是用来保存属性信息和这个属性的值,这些可用于ValueAnimator和ValueAnimator,这个值可不是一个值,而是一些值,例如上面示例中values设置为了[0,500],那么0,500将会被PropertyValuesHolder保存起来。一般情况下临时保存数据,常使用集合获取类似集合的东西,那么它是如何保存的呢,继续来看setValues函数。

再调用setValues函数之前,先调用了PropertyValuesHolder.ofFloat,

PropertyValuesHolder#ofFloat如下:

public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
        return new FloatPropertyValuesHolder(property, values);
}

说明,在这里创建一个FloatPropertyValuesHolder实例,FloatPropertyValuesHolder为PropertyValuesHolder内部类,很明显保存属性的值的类型为float类型,经过一系列调用,最终还会调用其父类的setFloatValues方法:

PropertyValuesHolder#setFloatValues:

 public void setFloatValues(float... values) {
        mValueType = float.class;
        //mKeyframes为Keyframes类型,似乎看到光明了,KeyframeSet很像一个set集合,
       //我们知道无论是list家族还是set家族,它们最终都是由Iterable派生过来的,
       //而KeyframeSet不是,那么它就不是类似集合的东西了,还得继续往下看
        mKeyframes = KeyframeSet.ofFloat(values);
}
KeyframeSet#ofFloat,如下:
 
public static KeyframeSet ofFloat(float... values) {
        boolean badValue = false;
        int numKeyframes = values.length;
        //
        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
            //  //把float类型的values转为FloatKeyframe保存起来,FloatKeyframe是什么东东,先不管不影响理解
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
            keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
            if (Float.isNaN(values[0])) {
                badValue = true;
            }
        } else {
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
            //把float类型的values转为FloatKeyframe保存起来,FloatKeyframe详见下文
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] =
                        (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
                if (Float.isNaN(values[i])) {
                    badValue = true;
                }
            }
        }
        .....//创建FloatKeyframeSet实例
        return new FloatKeyframeSet(keyframes);
}
说明,这个函数主要有两个作用:
(1)把float类型的values转为FloatKeyframe保存起来
(2)创建FloatKeyframeSet实例完成保存过程
接着看FloatKeyframeSet#构造函数:
 
public FloatKeyframeSet(FloatKeyframe... keyframes) {
        super(keyframes);
}
说明,我类个去,这个函数貌似没有干啥事,不急,不是通过super调用父类的构造函数嘛?,其父类是KeyframeSet,继续来看其父类的构造函数:
KeyframeSet#构造函数,如下:
 
List<Keyframe> mKeyframes; 
public KeyframeSet(Keyframe... keyframes) {
        ......
        //保存转换后的属性值
        mKeyframes = Arrays.asList(keyframes);
        ......
}
说明,哎,终于找到了,原来这个集合是个List集合,别急,继续回到上面的ValueAnimator#setFloatValues函数,在这里函数中上文提到调用了setValues函数,那么继续看看它做了什么事情。
ValueAnimator#setValues,如下:
HashMap<String, PropertyValuesHolder> mValuesMap;
public void setValues(PropertyValuesHolder... values) {
        int numValues = values.length;
        mValues = values;
       //好吧,把刚刚得到的FloatPropertyValuesHolder实例保存到了集合mValuesMap中
        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
        for (int i = 0; i < numValues; ++i) {
            PropertyValuesHolder valuesHolder = values[i];
            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
}
说明,好吧,转了一圈,最终保存到了ValueAnimator的成员变量mValuesMap中,那么总结一下属性保存的过程:首先在valueAnimator中调用PropertyValuesHolder#ofFloat函数,在这个函数中PropertyValuesHolder操作着KeyframeSet把属性值values转为Keyframe对象保存起来;接着把转换过的Keyframe对象封装到PropertyValuesHolder的mKeyframes变量中,返回PropertyValuesHolder子类实例,最好把该实例保存到了ValueAnimator的成员变量mValuesMap中。

四 估值器和插值器 

      对于给定一个范围的值,例如上面例子中ValueAnimator.ofFloat(0, 500),它给定的变化范围为[0, 500],那么在这个范围内到底是如何变化的呢?可以是线性变化,可以是加速变化,可以是减速变化,在内部已经为我们定义好了几种变化方式,我们可以根据情况来进行使用,接下来看看时间插值器和类型估值器,但在说这个之前先了解一个概念:时间因子。

1 时间流逝的百分比

      在帧刷新延迟中有提到默认10ms刷新一次,假设时间间隔40ms,一个对象的X坐标要在40ms内从0移动到40 pixel.按默认的10ms刷新一次,这个对象会移动4次,每次移动40/4=10pixel,第一次移动其时间流逝的百分比就是10/40 = 0.25,第二次移动20/40 = 0.5,第三、四次时间流逝的百分比分别为0.75、1,足见随着时间的流逝,其百分比逐渐增加直到1.有的资料上把时间流逝的百分比称为时间因子,个人感觉使用时间流逝的百分比描述更好些

时间插值器

TimeInterpolator中文翻译为时间插值器,它的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有LinearInterpolator(线性插值器:匀速动画)、AccelerateDecelerateInterpolator(加速减速插值器:动画两头慢中间快)和DecelerateInterpolator(减速插值器:动画越来越慢)等;接下来看看它们是如何实现的,这里以LinearInterpolator为例。

TimeInterpolator源码分析:

/**
 * A time interpolator defines the rate of change of an animation. This allows animations
 * to have non-linear motion, such as acceleration and deceleration.
 */
public interface TimeInterpolator {

    /**
     * 根据时间流逝的百分比来计算出当前属性值改变的百分比
     * Maps a value representing the elapsed fraction of an animation to a value that represents
     * the interpolated fraction. This interpolated value is then multiplied by the change in
     * value of an animation to derive the animated value at the current elapsed animation time.
     *
     * @param input A value between 0 and 1.0 indicating our current point
     *        in the animation where 0 represents the start and 1.0 represents
     *        the end 。   
     *    input的值0-1 0表示开始,1 表示结束 即时间流逝的百分比
     * @return The interpolation value. This value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets. 
     *       返回当前属性值改变的百分比 不同的算法不同,例如直线插值器,直接返回input 
     *       又如加速减速插值器 按照公式(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f计算
     */
    float getInterpolation(float input);
}

说明,TimeInterpolator很简单只有一个函数getInterpolation,这个函数以时间流逝的百分比为参数,然后根据这个参数通过不同的公式计算出当前属性值的变化的百分比,并且一切的插值器最终都是由TimeInterpolator派生的,在ValueAnimator中函数getAnimatedFraction,可以获取这个百分比:

private float mCurrentFraction = 0f;
public float getAnimatedFraction() {
        return mCurrentFraction;
}

LinearInterpolator源码如下:

@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        //很简单,线性插值器直接返回的是时间流逝的百分比,默认的情况下这个方法每10ms就会被调用一次
        return input;
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        //调用插值器工厂创建插值器实例
        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
    }
}

说明,LinearInterpolator继承了BaseInterpolator,实现了接口NativeInterpolatorFactory,NativeInterpolatorFactory只有一个方法就是createNativeInterpolator用来创建一个插值器实例,BaseInterpolator也没有处理什么业务逻辑,只是对Interpolator简单扩充,而Interpolator就继承了刚刚说的TimeInterpolator,BaseInterpolator源码如下:

abstract public class BaseInterpolator implements Interpolator {
    private @Config int mChangingConfiguration;
    public @Config int getChangingConfiguration() {
        return mChangingConfiguration;
    }
    void setChangingConfiguration(@Config int changingConfiguration) {
        mChangingConfiguration = changingConfiguration;
    }
}

值的一提的就是这个:

   @Override
    public long createNativeInterpolator() {
        //调用插值器工厂创建插值器实例
        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
    }

接着看NativeInterpolatorFactoryHelper什么鬼?

public final class NativeInterpolatorFactoryHelper {
    private NativeInterpolatorFactoryHelper() {}

    public static native long createAccelerateDecelerateInterpolator();
    public static native long createAccelerateInterpolator(float factor);
    public static native long createAnticipateInterpolator(float tension);
    public static native long createAnticipateOvershootInterpolator(float tension);
    public static native long createBounceInterpolator();
    public static native long createCycleInterpolator(float cycles);
    public static native long createDecelerateInterpolator(float factor);
    public static native long createLinearInterpolator();
    public static native long createOvershootInterpolator(float tension);
    public static native long createLutInterpolator(float[] values);
}

说明,好吧,全是native函数,这些函数全是用来创建插值器实例的,例如上面调用的是createNativeInterpolator创建线性插值器,这个些最终是在com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp(源码点击查看)文件中实现的,那么就看看它的createNativeInterpolator函数,如下:

com_android_internal_view_animation_NativeInterpolatorFactoryHelper#createLinearInterpolator:

static jlong createLinearInterpolator(JNIEnv* env, jobject clazz) {
    return reinterpret_cast<jlong>(new LinearInterpolator());
}

说明,很惊奇的发现,转一圈最后还是通过LinearInterpolator的构造函数创建的实例,那么为什么不直接在本地通过new关键字创建这样的实例,反而跑到底层去创建呢,这样做的好处呢?猜想,可能和性能有关吧,毕竟是移动端,都不希望在本地处理太多的东西,而getInterpolation在默认的情况每10ms调用一次,调用频率蛮高的,其内部就不只是表面看起来那么简单了,到此LinearInterpolator分析完毕,对于AccelerateDecelerateInterpolator等插值器除了当前属性值变化率的计算公式不同,其他都一样,就不再累述了。

类型估值器

      TypeEvaluator的中文翻译为类型估值算法,它的作用是根据当前属性改变的百分比来计算改变后的属性值,系统预置的有IntEvaluator(针对整型属性)、FloatEvaluator(针对浮点型属性)和ArgbEvaluator(针对Color属性)。

      我们知道插值器的作用就是返回当前属性改变的百分比,这个百分比我们可以通过重写getInterpolation来自定义。其实真正的变化后的值是从估值器来得到的,和上面一样我们也从IntEvaluator开始。

TypeEvaluator源码如下:

public interface TypeEvaluator<T> {
    /**
     * 根据当前属性改变的百分比来计算改变后的属性值
     * @param fraction   当前属性变化率
     * @param startValue 属性起始值.
     * @param endValue   属性结束值.
     * @return 当前的属性值.
     */
    public T evaluate(float fraction, T startValue, T endValue);

}

IntEvaluator源码如下:

public class IntEvaluator implements TypeEvaluator<Integer> {

    /**
     * 根据当前属性改变的百分比来计算改变后的属性值
     * @param fraction   当前属性变化率
     * @param startValue 属性起始值.
     * @param endValue   属性结束值.
     * @return 当前的属性值.
     */
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        //根据公式计算出当前的属性值
        return (int)(startInt + fraction * (endValue - startInt));
    }
}

说明,实现TypeEvaluator回调接口,实现evaluate方法,返回当前属性值,上述算法很简单,evaluate的三个参数分别表示:估值小数、开始值和结束值,其中的估值小数就是上面getInterpolation的返回值,开始值就是变化值的开始,结束值就是变化值的结束,对应上面例子ValueAnimator.ofFloat(0, 500),开始值为0,结束值为500,通过这三个参数,最终计算出变化后的值,然后将这个值返回去,我们最终得到的就是这个值,然后对指定对象的属性进行设置,这样来实现指定属性值的变化,从而实现了动画效果。

4 添加监听

        最后,我们如何得到这个变化后的值呢?从上面的例子中我们可以看到,我们只需要使用ValueAnimator的addUpdateListener函数来增加一个更新监听,当这个值变化之后,就会回调onAnimationUpdate函数,在传入的参数ValueAnimator对象中使用getAnimatedValue函数我们就可以获取到变化后的那个值,拿到这个变化后的值之后我们就可以动态的更新对象的属性值了,例如上面

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
            @Override
            public void onAnimationUpdate(ValueAnimator animation){
                //根据变化值来设置imageView对象的Y轴坐标,这样就实现了imageView的垂直移动
                img.setTranslationY((Float) animation.getAnimatedValue());
            }
        });

根据变化值来设置imageView对象的Y轴坐标,这样就实现了imageView的垂直移动。

当然也可以对动画过程进行监听,如下:

animator.addListener(new Animator.AnimatorListener() {
            //动画开始
            @Override
            public void onAnimationStart(Animator animation) {
                
            }
            //动画结束
            @Override
            public void onAnimationEnd(Animator animation) {

            }
            //动画取消
            @Override
            public void onAnimationCancel(Animator animation) {

            }
            //动画重复
            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        

或者:

animator.addPauseListener(new Animator.AnimatorPauseListener() {
            //动画暂停
            @Override
            public void onAnimationPause(Animator animation) {
                
            }
            //动画重启
            @Override
            public void onAnimationResume(Animator animation) {

            }
        });

五 keyframe

      一个 keyframe 对象由一对time  / value 的键值对组成,可以为动画定义某一特定时间的特定状态, Animator 传入的一个个参数映射为一个个 keyframe ,存储 相应的动画的触发时间 属性值

      也就是说我们可以使用Keyframe来为某个时间点指定一个变化值,在前面。。。我们说过,属性动画是使用时间流逝的百分比来计算属性值的变化的,我们为动画设置过持续时间Duration,这样通过Keyframe我们就可以在这个持续时间的某个时间点来设置一个具体的属性值,当到达这个时间点的时候,就会为对应的属性设置这个变化值。

另外我们需要注意的是上面所说的那个变化值,它是一个float值,这个值对应的就是插值器getInterpolation函数返回的那个值乘以(values起始值-values值)再加上values起始值【参见类估值器】,对应的变化值类型有以下几种:

public static Keyframe ofInt(float fraction, int value)

public static Keyframe ofInt(float fraction)

public static Keyframe ofFloat(float fraction, float value)

public static Keyframe ofFloat(float fraction)

public static Keyframe ofObject(float fraction, Object value)

public static Keyframe ofObject(float fraction)

说明,上面基本就是用来指定time / value 的键值对,其中的time就是时间因子,value就是变化值,它的意思就是fraction这个时间变化百分比对应的value变化值。

      我们可以指定多个fraction / value 的键值对,也就是创建多个Keyframe对象,将它们传入上面的PropertyValuesHolder.ofKeyframe函数中,就相当于我们在多个时间变化百分比分别指定了不同的变化值,所以在不同的时间点,会为指定的属性设置对应的属性变化值,这样就是实现了动画。

示例:

Keyframe keyframe1 = Keyframe.ofFloat(0f, 0f);
Keyframe keyframe2 = Keyframe.ofFloat(.5f, 200.0f);
Keyframe keyframe3 = Keyframe.ofFloat(1f, 0f);

PropertyValuesHolder property = PropertyValuesHolder.ofKeyframe("translationX", keyframe1, keyframe2, keyframe3);
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(imageView, property);

它还有很多的子类供我们使用如ObjectKeyframe、IntKeyframe、FloatKeyframe。


参考:







  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值