属性动画可以在以下几个方面对动画进行设置:
- Duration:动画持续时间,默认是300ms。
- Time interpolation:插值器,可以指定属性值如何根据动画当前播放时间进行变换。
- Repeat count and behavior:重复次数和重复方式,正向或者逆向重复。
- Animator sets:动画集合,可以对动画进行分类组合,指定播放顺序或者播放延时。
- Frame refresh delay:可以指定多久更新一次动画的帧,默认是10ms更新一次,但是应用更新动画的速度很大程度上取决于系统的繁忙程度,以及系统可以多快响应内含的定时器。
工作原理
ValueAnimator这个类记录着动画的计时,还封装了TimeInterpolator和TypeEvaluator两个类。TimeInterpolator用来计算时间到动画执行比例的变换,TypeEvaluator用来计算变化的属性值。
当ValueAnimator调用start()之后,ValueAnimator会根据动画时长和已流逝时间来计算时间流逝百分比。计算完时间流逝百分比后,ValueAnimator就会调用当前的TimeInterpolator来计算插值调整过后的实际百分比,然后根据实际百分比调用TypeEvaluator来计算当前的属性值。
ValueAnimator的使用
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();
// 自定义类型求值器
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();
// 监听值的变化
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator updatedAnimation) {
float animatedValue = (float)updatedAnimation.getAnimatedValue();
textView.setTranslationX(animatedValue);
}
});
ObjectAnimator的使用
ObjectAnimator是ValueAnimator的子类,帮我们把ValueAnimator改变后的值设置到对象上面,省去设置监听的步骤。
ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();
如果要让ObjectAnimator可以正常更新属性值,必须要满足以下几个条件:
- 所要设置动画的对象属性必须有对应的setter方法,如果没有的话,可以采取三种方案之一:1. 有权限修改这个类的话直接加一个setter方法 2. 使用一个包装类,包装类包含setter,然后再把设置的值转给目标对象属性。3. 使用ValueAnimator。
- 如果ObjectAnimator的工厂方法中values…可变参数的个数是一个,那这个参数值默认是动画的结束值,因此该对象属性必须有一个对应的getter方法来获取动画的初始值。
- 如果有getter,和setter操作的属性类型必须是一样的。
- 根据执行动画的特定属性或者对象,可能需要调用invalidate()来强制重绘,可以在onAnimationUpdate()回调方法中操作。
使用动画集合播放多个动画
可以使用AnimatorSet来控制多个动画的播放,AnimatorSet可以自己嵌套自己。
AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();
动画监听
- Animator.AnimatorListener
- onAnimationStart():动画开始时调用
- onAnimationEnd():动画结束时调用
- onAnimationRepeat():动画重复时调用
- onAnimationCancel():动画取消时调用,动画取消时也会调用onAnimationEnd()
- ValueAnimator.AnimatorUpdateListener
- onAnimationUpdate():动画的每一帧都会调用这个回调,可以调用ValueAnimator对象的getAnimatedValue()来获取当前值。
如果不想要实现AnimatorListener中的所有回调,可以继承AnimatorListenerAdapter来选择自己需要的回调去重写。
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
balls.remove(((ObjectAnimator)animation).getTarget());
}
ViewGroup的布局动画
属性动画在给View对象提供动画的同时,也可以用来给ViewGroup的布局变化设置动画。
布局动画是通过LayoutTransition类来实现。布局动画指的是ViewGroup当中View的添加或删除,或者可见性的改变,对相应的View进行动画出现或者消失。其他View也可以动画移动到新的位置上。
通过调用LayoutTransition对象的setAnimator()方法来设置动画,可以选择以下的常量来设置:
- APPEARING:表示这个动画用来作用在正在显示的View。
- CHANGE_APPEARING :表示这个动画用来作用在因为其他新显示View而改变位置的View上。
- DISAPPEARING :表示这个动画用来作用在正在消失的View。
- CHANGE_DISAPPEARING :表示这个动画用来作用在因为其他新消失View而改变位置的View上。
如果要使用系统默认的布局动画,只要加上animateLaoutChanges=true即可:
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/verticalContainer"
android:animateLayoutChanges="true" />
使用TypeEvaluator
系统已经实现的TypeEvaluator包括IntEvaluator、FloatEvaluator、ArgbEvaluator,如果要对自定义类型进行动画,那就要实现TypeEvaluator接口:
public class FloatEvaluator implements TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}
使用插值器
插值器是用来计算属性值如何随着动画流逝比例进行变化的。系统提供的插值器在android.view.animation包中,如果要自定义插值器可以实现TimeInterpolator接口。
两种系统插值器的实现:
LinearInterpolator:
public float getInterpolation(float input) {
return input;
}
AccelerateDecelerateInterpolator:
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
指定关键帧
一个Keyframe对象用来指定某个时间点的具体属性值,两个Keyframe之间还可以指定interpolator来控制中间的变化行为。
具体使用就是先调用工厂方法获取Keyframe对象,然后调用PropertyValuesHolder的静态方法ofKeyframe()获取PropertyValuesHolder对象,再调用ObjectAnimator的静态ofPropertyValuesHolder()获取ObjectAnimator。
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);
使用ViewPropertyAnimator
ViewPropertyAnimator用来对View的多个属性同时进行动画。组合多个ObjectAnimator也可以进行多属性动画,不过会比较麻烦。
1. 单纯ObjectAnimator进行多属性动画
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();
2. 配合ViewPropertyAnimator进行多属性动画
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
另一种更简洁的用法:
myView.animate().x(50f).y(100f);
ObjectAnimator
ObjectAnimator有个特殊的rotateX和rotateY属性,是在三维维度上进行动画的。
在XML中声明动画
为了跟补间动画区分开来,属性动画的XML文件目录是res/animator/。
以下为类及其对应的XML标签:
- ValueAnimator :
<animator>
- ObjectAnimator :
<objectAnimator>
- AnimatorSet :
<set>
XML示例:
<set android:ordering="sequentially">
<set>
<objectAnimator
android:propertyName="x"
android:duration="500"
android:valueTo="400"
android:valueType="intType"/>
<objectAnimator
android:propertyName="y"
android:duration="500"
android:valueTo="300"
android:valueType="intType"/>
</set>
<objectAnimator
android:propertyName="alpha"
android:duration="500"
android:valueTo="1f"/>
</set>
XML动画的使用:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.anim.property_animator);
set.setTarget(myObject);
set.start();
ValueAnimator示例:
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:valueType="floatType"
android:valueFrom="0f"
android:valueTo="-100f" />
ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this,
R.animator.animator);
xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator updatedAnimation) {
float animatedValue = (float)updatedAnimation.getAnimatedValue();
textView.setTranslationX(animatedValue);
}
});
xmlAnimator.start();
动画对UI性能的影响
更新UI的动画会对UI每一帧的渲染增加额外的工作,是在渲染管道的动画阶段进行的,可以通过启用Profile GPU Rendering来查看动画阶段的性能表现。