属性动画,就是通过控制对象中的属性值产生的动画。属性动画是目前最高级的2D动画系统。
在API Level 11中添加。Property Animation号称能控制一切对象的动画,包括可见的和不可见的。
但是,日常开发中我们一般都是对UI定制动画。
使用ObjectAnimator
ObjectAnimator是其中比较容易使用的一个动画类,它继承自ValueAnimator,
说比较容易使用是因为它在动画启动后自动监视属性值的变化并把值赋给对象属性,
而ValueAnimator则只监视属性值的变化,但不会自动在属性中应用该值,因此我们需要手动应用这些值。
代码:
//创建一个水平移动的动画对象,从位置0到300平移 final ObjectAnimator translation = ObjectAnimator.ofFloat(tv, "translationX", 0f, 300f); translation.setDuration(1500);
只需两行就可以创建一个简单可运行的移动动画。
当然还有其他setxxx()方法可以控制动画的高级行为。
例如:添加一个插值器等
translation.setInterpolator(new AccelerateDecelerateInterpolator());
运行效果:
使用ValueAnimator
ValueAnimtor动画的创建基本上和ObjectAnimator一样,只是我们需要手动应用属性值
代码:
final ValueAnimator translation = ValueAnimator.ofFloat(0f, 300f); translation.setDuration(1500);
有点不一样的是在创建对象的时候,ValueAnimator无需指定属性名称
只需指定动画的执行范围。
上面我们已经说了,ValueAniamtor不会自动应用属性值,因此我们需要添加一个动画监听器
translation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { tv.setTranslationX((float) animation.getAnimatedValue()); } });
tv是我们需要执行动画的对象,这里是一个TextView对象。
要使tv对象的动画执行,我们就要在监听器中使用对应setxxx方法更新tv的属性值,
而这些值我们可以使用animation对象的getAnimatedValue方法获得。
使用动画监听器执行不同的任务
通过监听器我们可以在动画的不同状态执行不同的任务
translation.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { } });
例如:我们可以在前一个动画执行完毕之后执行另一个动画
translation.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { ObjectAnimator rotation = ObjectAnimator.ofFloat(tv, "rotation", 0f, 360f); rotation.setDuration(1500); rotation.start(); } });
执行效果:
使用PropertyValuesHolder控制多个对象属性
例如我们对某个对象进行缩放控制的时候,就需要同时改变对象的x轴和y轴的值
这个时候我们就可以使用ObjectAnimator的ofPropertyValuesHolder()方法配合PropertyValuesHolder对象
//把按钮放大1.5倍 PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("scaleX", 1f, 1.5f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleY", 1f, 1.5f); final ObjectAnimator scale = ObjectAnimator.ofPropertyValuesHolder(btnStart, pvhX, pvhY); final ValueAnimator translation = ValueAnimator.ofFloat(0f, 300f); translation.setDuration(1500); btnStart.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { //执行放大动画 scale.start(); translation.start(); } });
运行效果:
使用Keyframe对象控制多属性动画
Keyframe对象对多属性动画的控制更加灵活,因为我们可以更好地控制每个时间段执行的动画距离
Keyframe keyframe0 = Keyframe.ofFloat(0f, 0); Keyframe keyframe1 = keyframe.ofFloat(.3f, 100); Keyframe keyframe2 = keyframe.ofFloat(.4f, 200); Keyframe keyframe3 = keyframe.ofFloat(1f, 200);
PropertyValuesHolder pvhM = PropertyValuesHolder.ofKeyframe("translationX", keyframe0,keyframe1, keyframe2,keyframe3); final ObjectAnimator trans = ObjectAnimator.ofPropertyValuesHolder(tv, pvhM); trans.setDuration(1500); final ValueAnimator translation = ValueAnimator.ofFloat(0f, 300f); translation.setDuration(1500); btnStart.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { //执行放大动画 scale.start(); trans.start(); //translation.start(); } });
代码分析:
每个keyframe变量第一个参数执行总时间0%~100%,0表示动画未执行,1代码动画执行完毕
keyframe0代表动画在0%时的位置
keyframe1代表动画在30%时所处的位置
keyframe2代表动画在最后一个时间段所处的位置
keyframe3代表在200个单位距离时动画完成已经停止,加上这一行是为了动画停止后对象不会消失
运行效果:
上面的图片虽然不是看得很清楚,但是可以看见在最后一段时间动画突然加快。
原因是我们在定义最后一个keyframe的时候是用40%的时间走200个单位距离,
即它要在最短的时间走最多的距离,所以它必须加快速度才能完成这个任务。
不加keyframe3时的运行效果:
完整代码:
package com.whathecode.propertyanimation; import android.animation.Animator; import android.animation.Keyframe; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final TextView tv = (TextView) findViewById(R.id.tv); Button btnStart = (Button) findViewById(R.id.start); /* //创建一个水平移动的动画对象,从位置0到300平移 final ObjectAnimator translation = ObjectAnimator.ofFloat(tv, "translationX", 0f, 300f); translation.setDuration(1500); */ //把按钮放大1.5倍 PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("scaleX", 1f, 1.5f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleY", 1f, 1.5f); final ObjectAnimator scale = ObjectAnimator.ofPropertyValuesHolder(btnStart, pvhX, pvhY); Keyframe keyframe0 = Keyframe.ofFloat(0f, 0);
Keyframe keyframe1 = keyframe.ofFloat(.3f, 100); Keyframe keyframe2 = keyframe.ofFloat(.4f, 200); Keyframe keyframe3 = keyframe.ofFloat(1f, 200); PropertyValuesHolder pvhM = PropertyValuesHolder.ofKeyframe("translationX", keyframe0,keyframe1, keyframe2,keyframe3); final ObjectAnimator trans = ObjectAnimator.ofPropertyValuesHolder(tv, pvhM); trans.setDuration(1500); final ValueAnimator translation = ValueAnimator.ofFloat(0f, 300f); translation.setDuration(1500); btnStart.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { //执行放大动画 scale.start(); trans.start(); //translation.start(); } }); translation.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { ObjectAnimator rotation = ObjectAnimator.ofFloat(tv, "rotation", 0f, 360f); rotation.setDuration(1500); rotation.start(); } }); translation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { tv.setTranslationX((float) animation.getAnimatedValue()); } }); } }