属性动画(Property Animation)
内容如下:
- 属性动画与补间动画的不同
- 属性动画工作原理
- API 概述
- 计算器 Evaluators
- 插值器 Interpolators
- 使用ValueAnimator
- 使用ObjectAnimator
- AnimatorSet
- 动画监听
- 视图容器中布局的动画改变
- 使用TypeEvaluator
- 使用插值器 (Interpolators)
- 关键帧
- ViewPropertyAnimator
- 声明XML
属性动画与补间动画的不同
补间动画只提供了运动可视对象的能力,如果你想操作非可视对象,就不得不自己用代码实现。实际上,补间动画具有局限性,它只能够使一个视图对象的几个方面做运动,比如:视图的缩放、旋转,而不能操作背景颜色等等。
补间动画另一个缺点是:它只能够改变视图绘制的位置,并不能改变它的真实位置。例如:你在屏幕中运动一个按钮,按钮将被正确的绘制,但是当你点击当前位置的按钮时,按钮并不会有任何改变。所以你不得不通过实现自己的逻辑来操作它。
对于属性动画而言,这些限制被全部移除。你能够使任何对象(可视和非可视)的任何属性运动,并且对象自身被修改。属性动画就是通过不停修改属性值实现动画效果的。属性动画在实现动画方面更加强大。你可以根据需求将动画分派给对象的属性,比如:颜色、位置、大小;你也可以定义动画的差值器以及多动画的同步。
不管怎样,补间动画的创建可以用更少的时间和更少的代码。如果补间动画能够完成你需要的每件事,或者代码中已存在的补间动画代码能够实现你的需求。你就没必要使用属性动画。通过不同的使用场景来确定使用两种动画系统的哪一种。
属性动画工作原理
图1描述了一个假想的对象,沿x轴运动,表示它在屏幕上的水平位置。在40ms内运动40px,每10毫秒(默认帧刷新率),对象水平移动10像素。到40ms时,动画结束。对象停在40px处。这是一个线性插值动画的例子,表示对象匀速运动。
还可以指定具有非线性插值的动画。图2假设一个对象,在动画开始时加速,然后减速动画结束时。该对象仍然在40ms移动40px,但非线性。
属性动画系统的重要组件如何计算上面提到的动画:
ValueAnimator 对象保持跟踪动画的时间,比如动画的已运动时间和动画的当前值。
TimeInterpolator 定义动画的插值器。
TypeEvaluator 定义如何计算动画属性的值。
开始一个动画:
- 创建一个ValueAnimator并给它的开始属性值、结束属性值以及动画的持续时间。
- 调用start()开始动画。
- ValueAnimator计算一个在0和1之间的逝去分数,基于动画的持续时间和经过的多少时间。该分数表示动画完成的时间百分比,0表示0%,1表示100%。
- 当ValueAnimator完成计算一个逝去分数,它调用当前设置的TimeInterpolator计算插值分数。
- 当插值分数计算完成,ValueAnimator调用合适的TypeEvaluator,计算该属性的值,基于插值分数、起始值和动画结束值。
API 概述
ValueAnimator 属性动画的计时引擎,通过属性值进行动画处理。包括计算动画值的核心功能,动画随时间变化的细节,动画重复监听以及更新时间,自定义evaluate。属性动画包括两部分:1.计算动画值;2.对正在进行动画的对象设置这些值。ValueAnimator不能直接完成第二部分,所以必须在UpdateListener中更新属性值。
ObjectAnimator ValueAnimator的子类,可以直接设置目标对象和属性进行动画。ObjectAnimator使用起来更方便,也有限制,对于进行的动画的属性必须要有setter,getter的访问方法。
AnimatorSet 提供一种将多动画分组并使之关联运行的机制。可以使动画同时执行,顺序执行,延时执行。
估算器 Evaluators
- IntEvaluator 计算int型属性
- FloatEvaluator 计算float型属性
- ArgbEvaluator 计算16进制的颜色值
- TypeEvaluator 自定义计算器(更多见下文)
插值器 Interpolators
- AccelerateDecelerateInterpolator 先加速后减速
- AccelerateInterpolator 加速
- AnticipateInterpolator 向相反运动一小段后再开始运行
- AnticipateOvershootInterpolator 前后都超出一小段
- BounceInterpolator 结束时振动结束
- CycleInterpolator 往返运行动画
- DecelerateInterpolator 减速运行
- LinearInterpolator 匀速运行
- OvershootInterpolator 结束时超出一小段
- TimeInterpolator 自定义
使用ValueAnimator
通过调用ofInt(),ofFloat(),ofObject()方法获得一个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();
上述代码,当调用start()时,ValueAnimator 将在1s内计算0-1之间的动画值。它只是在计算一个值并没有影响到一个具体的对象。你需要实现ValueAnimator.AnimatorUpdateListener这个接口,通过调用getAnimatedValue()方法可以获得特定帧的计算值,通过这个值可以对目标对象进行需要的变换。
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator updatedAnimation) {
// You can use the animated value in a property that uses the
// same type as the animation. In this case, you can use the
// float value in the translationX property.
float animatedValue = (float)updatedAnimation.getAnimatedValue();
textView.setTranslationX(animatedValue);
}
});
使用ObjectAnimator
ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();
拥有setter,getter方法的属性都可以进行动画操作。
经常操作的属性:
“alpha” 透明度;
“translationY” 沿Y轴平移;
“translationX” 沿X轴平移;
“scaleX” 横向缩放;
“scaleY” 纵向缩放;
“rotation” 平面旋转;
“rotationX” 关于X轴旋转;
“rotationY” 关于Y轴旋转;
AnimatorSet
你可以使用AnimatorSet控制多个Animator的播放顺序,同时播放,顺序播放,谁在前谁在后等。可以嵌套使用。例:
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();
常用方法:
playSequentially (Animator…) 顺序播放;
playTogether (Animator…) 一起播放;
Builder方式:
play(Animator a);
创建一个Builder对象;
例1:有四个动画anim1, amin2, anim3,anim4。anim1和anim2同时播放;当anim2播放结束时播放anim3;当anim3播放结束时播放anim4。
AnimatorSet s = new AnimatorSet();
s.play(anim1).with(anim2);
s.play(anim2).before(anim3);
s.play(anim4).after(anim3);
动画监听
Animator.AnimatorListener
* onAnimationStart() 动画开始时
* onAnimationEnd() 动画结束时
* onAnimationRepeat() 动画重复时
* onAnimationCancel() 动画取消时,同时调用onAnimationEnd()
ValueAnimator.AnimatorUpdateListener
* onAnimationUpdate() 每帧动画都将被调用
通过getAnimatedValue()方法获取动画值。
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());
}
)};
视图容器中布局的动画改变
使用TypeEvaluator
如果你想以系统未知的方式运动,你可以通过实现TypeEvaluator接口来创建。实现TypeEvaluator接口只需要实现一个方法evaluate()。如下:
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);
}
}
例:使TextView沿正弦曲线运动
创建包装类:
private class TextHolder
private TextView view;
public TextHolder(TextView textView){
view=textView;
}
public XYHolder getXY() {
return new XYHolder(view.getX(),view.getY());
}
public void setXY(XYHolder xyHolder) {
view.setX(xyHolder.getX());
view.setY(xyHolder.getY());
}
}
估值器:
public class SineTypeEvaluator implements TypeEvaluator<XYHolder> {
@Override
public XYHolder evaluate(float fraction, XYHolder startValue, XYHolder endValue) {
float xHolder=startValue.getX()+fraction*(endValue.getX()-startValue.getX());
float yHolder=(float) (300*Math.sin(xHolder/100)+endValue.getY());
return new XYHolder(xHolder,yHolder );
}
}
开始动画:
ObjectAnimator objectAnimator=ObjectAnimator.ofObject(new TextHolder(mText1),"xY",new
SineTypeEvaluator(),new XYHolder(0f,400f),new XYHolder(628f,400f));
objectAnimator.setDuration(2000);
objectAnimator.start();
使用插值器
一个随时间计算动画值的函数
AccelerateDecelerateInterpolator
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
LinearInterpolator
public float getInterpolation(float input) {
return input;
}
关键帧
例如:
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提供一种更简单的多属性运动的方式。如下对比:
使用多个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();
使用一个ObjectAnimator:
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
使用ViewPropertyAnimator:
myView.animate().x(50f).y(100f);</pre>
声明XML
将xml文件保存在res/animator/ 目录下
动画类与xml标签的对应关系:
ValueAnimator -
ObjectAnimator -
AnimatorSet-
下面的示例依次播放两组对象动画,第一个动画集合同时播放两个对象动画:
<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>
运行上述动画:
AnimatorSet set = (AnimatorSet)
AnimatorInflater.loadAnimator(myContext,R.anim.property_animator);
set.setTarget(myObject);
set.start();
在XML中声明ValueAnimator:
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:valueType="floatType"
android:valueFrom="0f"
android:valueTo="-100f" />
ValueAnimator必须实现AnimatorUpdateListener,如下:
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();
推荐阅读
-
Android Animation之TranslateAnimation(平移动画)
-
深入讲解PHP的Yii框架中的属性(Property)
-
图文详解Android属性动画
-
Android开发之图形图像与动画(二)Animation实现图像的渐变/缩放/位移/旋转
-
Android开发之图形图像与动画(三)Animation效果的XML实现
-
css3的动画特效之动画序列(animation)
-
基于CSS3 animation动画属性实现轮播图效果
-
CSS3与动画有关的属性transition、animation、transform对比(史上最全版)
-
举例讲解Objective-C中@property属性的用法
-
Android 属性动画ValueAnimator与插值器详解