欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

属性动画(Property Animation)

程序员文章站 2022-03-01 20:51:27
...

属性动画的特性

1.Duration(持续时间)
2.时间插着器(Time interpolation):指定这个属性的值变化的方式。比如,变化越来越快,或是越来越慢。
3.重复的次数和方式(Repeat count and behavior):你可以指定动画是否重复播放、重复的次数、重复的方式(从头到尾正向重复还是从尾到头反向重复)
4.动画集合(Animator sets):将多个动画组织成一个逻辑集合,你可以让这些动画同时播放,或是一个一个顺序播放,或是其他逻辑。
5.帧刷新频率(Frame refresh delay):即,属性的值得刷新频率。系统默认10ms,不过实际刷新频率,还是又系统的繁忙程度和系统时钟的快慢来决定的。

时间插值器(TimeInterpolator)

一句话总结时间插值器的作用:根据当前的时间进度比,计算出当前动画进度比例。

TimeInterpolator本身是一个接口,它的实现类决定了动画变化的速度。你如果不想让你的动画是线性匀速变化,比如加速或减速变化,就需要实现这个方法。

//接口代码
public interface TimeInterpolator { 
    float getInterpolation(float input);
}

getInterpolation()方法的参数:input = 过去的时间 / 总时间。表示动画的当前时间位置,0表示开始,1.0表示结束。
getInterpolation()的返回值则表示经过插值计算之后的值,一般在0-1之间,不过,这个返回值可能存在小于0和大于1的情况,这是为了实现越界反弹等动画效果。

线性插值器LinearInterpolator的getInterpolation()只是简单的将input返回,方法体如下

public float getInterpolation(float input) {
    return input;
}

加速插值器AccelerateInterpolator的getInterpolation()默认情况下回返回input的平方。

public float getInterpolation(float input) {
    if (mFactor == 1.0f) {
        return input * input;
    } else {
        return (float)Math.pow(input, mDoubleFactor);
    }
}

类型求值器(TypeEvaluator)

TypeEvaluator根据当前时间点的动画进度,计算出属性当前时间点的值。

在上面我们知道TimeInterpolator根据当前的时间进度比,计算出动画进度应有的比例。那么TypeEvaluator就是根据这个动画进度参数、属性初始值,属性最终值,就算出当前时间下的属性的值。

//TypeEvaluator接口代码
public interface TypeEvaluator<T> {
    //根据动画进度,属性起始值和结束值,计算出当前值
    public T evaluate(float fraction, T startValue, T endValue);
}

TypeEvaluator是用来计算属性值,它的实现类有IntEvaluator, FloatEvaluator, ArgbEvaluator等,分别用于不同类型的属性。比如你的属性是int类型的,则对应IntEvaluator

//IntEvaluator的实现
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
    int startInt = startValue;
    return (int)(startInt + fraction * (endValue - startInt));
}

Property Animation 和 View Animation的区别

  1. View Animation 只能用于View及View的子类的对象;Property Animation则没有这样的限制
    2.View Animation限制较多,一些动画,只能用于没有background的view,否则会打不到预期效果;如,缩放一个view时,这是缩小了view的外观,她的background并没有改变,所以如果background不是透明的,则会露馅。
    3.View Animation只是简单的改变View的绘制,并不会改变View本身。比如,你将一个button从位置1移动到位置2,但是触发点击事件的位置还是在位置1。Property Animation这不会出现这种情况。
  2. View Animation 使用的代码较少,启动所花费的时间也短,如果View Animation能够达到你想要的效果,你同样可以继续使用它。你可以根据情况的不同,选择使用哪种动画。

ValueAnimator

举个栗子

ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();

上面这段代码中,ValueAnimator在1000ms内,在0f-1f之间,不断地改变这个浮点类型的值,0f是开始值,1f是结束值。

ValueAnimator有工厂方法ofInt()、ofFloat()、ofObject()用于不同的类型。

对于自定义类型,你可以这样:

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();

在这段代码中,ValueAnimator在开始值startPropertyValue和结束值endPropertyValue之间,不断的计算属性值。

上面的代码并没有真正作用到一个对象上,除了属性值的改变,你观察不到任何变化。如果你想达到当属性值变化的同时,让某个对象(外观、内容等任意)也随之改变,你需要为这个ValueAnimator设置Animation Listeners.当属性动画时间周期内发生重要事件,比如帧刷新,此Listener可以通过getAnimatedValue()得到这个属性在此时的值,然后根据这个值,去做相应的操作,比如移动控件的位置,改变大小等。

ObjectAnimator

ObjectAnimator是ValueAnimator的子类。其让属性动画作用于任何对象都变得很简单,而不再需要实现ValueAnimator.AnimatorUpdateListener,因为这个对象的属性会自动的刷新。

举个栗子

ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();

ObjectAnimator会不断的改变对象foo的透明度。
foo对象的类中必须有setAlpha(float alpha)成员函数

ObjectAnimator注意事项

1.动画作用于的目标对象的属性必须有set<propertyName>()形式的setter方法。ObjectAnimator通过这个方法更新此属性的值。
2.如果你没有为参数values...提供起始值,比如,ObjectAnimator.ofFloat(foo, "alpha")或ObjectAnimator.ofFloat(foo, "alpha",1f)(提供一个值,则作为结束值),你必须提供getter方法,形如get<propertyName>(),如getAlpha();
3.getter和setter方法的参数类型必须和ObjectAnimator.ofXXX(targetObject,"propName",values...)的values的类型相同。
4.根据用于动画的属性的不同,你可能需要调用view的invalidate()方法强制在重绘view。你可以在onAnimationUpdate()中调用invalidate();比如,Drawable的color属性,只有当此Drawable重绘时才会引起屏幕刷新。

Animation Listeners

你可以用下面的Listener监听动画,一边在重要事件发生是,有所操纵。

Animator.AnimatorListener
ValueAnimator.AnimatorUpdateListener
AnimatorListenerAdapter -- 用与代替Animator.AnimatorListener

如果你不想实现AnimatorListener的所有方法,你可以使用AnimatorListenerAdapter代替,AnimatorListenerAdapter使用空方法实现了AnimatorListener的所有方法。

举个栗子

ValueAnimatorAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
    public void onAnimationEnd(Animator animation) {
        balls.remove(((ObjectAnimator)animation).getTarget());
    }
}

布局动画

属性动画同样可以想应用于View那样方便的应用于ViewGroup。通过调用ViewGroup#setLayoutTransition(LayoutTransition)方法为其设置

你可以通过使用LayoutTransition的方法[setAnimator()](http://developer.android.com/reference/android/animation/LayoutTransition.html#setAnimator(int, android.animation.Animator)),并传入一个 Animator和一个下面的常量(定义在LayoutTransition中)值作为参数,来定制用于ViewGroup的动画。

  • APPEARING
    指明这个动画是用于当某个条目显示出来时使用.
  • CHANGE_APPEARING
    指明当有条目显现时,这个动画用于其他item.
  • DISAPPEARING
    指明这个动画是用于当某个条目隐藏时使用.
  • CHANGE_DISAPPEARING
    指明当有条目隐藏时,这个动画用于其他item.

你可以在ViewGroup的xml中添加android:animateLayoutchanges="true"来为ViewGroup指定默认动画。
栗子

<LinearLayout
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:id="@+id/verticalContainer"
    android:animateLayoutChanges="true" />

指定关键帧 Keyframes

一个Keyframe对象有一个time/value对组成,用于指定在特定时间点,动画的状态。每一个关键帧都可以有它自己的插值器,用来控制前一个关键帧和此关键帧的关系。

  1. 你必须使用方法ofInt(), ofFloat(), 和ofObject()中的一个来获得一个关键帧实例。
  2. 然后调用PropertyValuesHolder的工厂方法[ofKeyframe()](http://developer.android.com/reference/android/animation/PropertyValuesHolder.html#ofKeyframe(android.util.Property, android.animation.Keyframe...))来获得一个PropertyValuesHolder对象。
  3. 然后,将PropertyValuesHolder对象作为ObjectAnimator.ofPropertyValuesHolder()方法的参数,即可构造出一个对象。

栗子

Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation);
rotationAnim.setDuration(5000ms);