你还可以指定具有非线性内插的动画。图2示出了对象在动画开始时加速,并且在动画结束时减速。 物体仍然在40毫秒内移动40像素,但是非线性。 一开始,这个动画加速到中途,然后从中途减速直到动画结束。 如图2所示,动画开头和结尾处的距离小于中间。
图2
我们来详细了解属性动画系统的重要组件如何计算动画,如图所示:
图3
ValueAnimator对象跟踪动画的时间,例如动画已运行多长时间,以及动画所属属性的当前值。ValueAnimator封装了TimeInterpolator,它定义了动画插值,还有一个TypeEvaluator,它定义了如何计算动画属性的值。例如,在图2中,使用的TimeInterpolator将为AccelerateDecelerateInterpolator,TypeEvaluator将为IntEvaluator。
要启动动画,创建一个ValueAnimator,并为其创建动画的属性、动画持续时间以及它的开始和结束值,然后调用
start()方法开始动画。在整个动画过程中,ValueAnimator根据动画的持续时间和经过多少时间,计算0到1之间的经过分数。经过的分数表示动画完成的时间百分比,0表示0%,1表示100%。 例如,在图1中,t = 10ms时的经过分数为0.25,因为总持续时间为t = 40ms。当ValueAnimator完成计算经过的分数时,它调用当前设置的TimeInterpolator,以计算内插分数(编者按:不是有个插值器么,定义了动画的快慢方式,比如匀速,先加速后减速)。将经过的时间分数映射到考虑到插值的新分数(编者按:最后根据这个新分数来计算实际的百分比,在上面是实际的运动距离)。例如,在图2中,由于动画缓慢加速,所以在t = 10 ms时,内插分数约为.15,小于经过的时间分数.25。 在图1中,插值分数总是与经过分数相同。
当计算插值分数时,ValueAnimator将根据插值分数,起始值和动画的结束值,调用相应的TypeEvaluator来计算您要动画化的属性值。 例如,在图2中,t = 10 ms时插值分数为.15,因此此时的属性值为.15 X(40 - 0)或6。
Android 的View Animation系统只提供了给View对象动画处理的功能,因此,如果你想为非View对象设置动画效果,则必须自己去实现相关代码。View Animation系统本身也有限制,它只将View的几个方面暴露出来让我们来实现动画,例如视图的缩放和旋转,但是并没有提供给我们背景颜色(编者按:背景颜色属于View的属性,我们可以使用属性动画来控制它)。
View Animation系统的另一个缺点是它只修改了View的绘制位置,而不是实际的View本身。例如,如果你通过View Animation系统实现一个按钮在屏幕上移动的动画,则动画可以正确绘制,但你可以单击按钮的实际位置不会更改(编者按:也就你点击的区域不会改变,你可以自己实现来看,虽然View移动了,但是点击区域还是原来的位置),因此你需要来处理此问题(编者按:如果你知识需要一个动画,这样就可以了)。
使用属性动画的话,就没有上面所说的问题。你可以对任何对象(视图和非视图)的任何属性进行动画处理,并且对象本身实际上进行了修改。属性动画系统在执行动画的方式也更加强大。在高级别上,你可以分配animators (编者按:这个地方官方给的是animators )给你想要执行动画的属性,例如颜色,位置或大小,并且可以定义动画的方面(aspects ),例如多个animators的插值(编者按:插值器)和同步。
然而,View Animation系统需要较少的时间来设置,并且需要较少的代码来写入。如果View Animation系统就可以完成了你想要的动画效果,则不需要使用属性动画。
如果使用情况出现的话(编者按:个人认为是如果需要的话),在不同情况下使用这两种动画系统也是有意义的。
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();
(编者按:这里只是对值进行了处理,并没有涉及到目标对象以及我们要操作的属性)ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();
(编者按:这里需要我们自己去计算实际的动画值,你传入的是object,系统又不知道该怎么去帮你计算,所以只能自行处理,这里是用MyTypeEvaluator继承自TypeEvaluator来进行处理)
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);
}
});
在onAnimationUpdate()方法中,您可以访问更新的动画值,并在其中一个视图的属性中使用它。
ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();
要使ObjectAnimator正确更新属性,必须执行以下操作:
一、正在动画的对象属性必须以set
例如,如果属性名称为foo,则需要具有setFoo()方法。如果此setter方法不存在,有三个选项:
1:如果有权这样做,将setter方法添加到类中。
2:使用有权更改的包装类,并使该包装器使用有效的setter方法接收该值并将其转发到原始对象。
3:改用ValueAnimator。
二:如果在ObjectAnimator的方法之一中为values ...参数只指定了一个值,那么那个值假定是动画的结束值。因此,您正在动画的对象属性必须具有用于获取动画起始值的getter函数。 getter函数必须以get
三:要动画的属性的getter(如果需要)和setter方法必须与您指定给ObjectAnimator的起始值和结束值有相同的类型。例如,如果构造以下ObjectAnimator,则必须具有targetObject.setPropName(float)和targetObject.getPropName(float):
ObjectAnimator.ofFloat(targetObject, "propName", 1f)
四:根据您所动画的属性或对象,您可能需要在视图上调用invalidate()方法来强制屏幕使用更新的动画值重新绘制自己。你可以在onAnimationUpdate()回调中执行此操作。例如,动画化Drawable对象的color属性只会导致在Drawable对象重新绘制时更新屏幕。View上的所有属性setter(如setAlpha()和setTranslationX())都会使View无效,因此在使用新值调用这些方法时,不需要使View无效。 有关侦听器的更多信息,请参阅有关动态侦听器的部分。
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
这四个方法。如果你不想实现这么多的话,比如你只需要一个的实现,你就可以使用
AnimatorListenerAdapter类,这个类继承自Animator.AnimatorListener接口和Animator.AnimatorPauseListener接口,这个接口里有两个方法
void onAnimationPause(Animator animation);
void onAnimationResume(Animator animation);
所以
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());
}
public void setAnimator(int transitionType, Animator animator) {
switch (transitionType) {
case CHANGE_APPEARING:
mChangingAppearingAnim = animator;
break;
case CHANGE_DISAPPEARING:
mChangingDisappearingAnim = animator;
break;
case CHANGING:
mChangingAnim = animator;
break;
case APPEARING:
mAppearingAnim = animator;
break;
case DISAPPEARING:
mDisappearingAnim = animator;
break;
}
}
将此属性设置为true将自动生成从ViewGroup添加或删除的视图以及ViewGroup中的其余视图的动画。
public interface TypeEvaluator {
/**
* This function returns the result of linearly interpolating the start and end values, with
* fraction
representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: result = x0 + t * (x1 - x0)
,
* where x0
is startValue
, x1
is endValue
,
* and t
is fraction
.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value.
* @param endValue The end value.
* @return A linear interpolation between the start and end values, given the
* fraction
parameter.
*/
public T evaluate(float fraction, T startValue, T endValue);
}
/**
* This evaluator can be used to perform type interpolation between float
values.
*/
public class FloatEvaluator implements TypeEvaluator {
/**
* This function returns the result of linearly interpolating the start and end values, with
* fraction
representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: result = x0 + t * (v1 - v0)
,
* where x0
is startValue
, x1
is endValue
,
* and t
is fraction
.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type float
or
* Float
* @param endValue The end value; should be of type float
or Float
* @return A linear interpolation between the start and end values, given the
* fraction
parameter.
*/
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}
(编者按:这个地方跟官方的不一样,这里我才用的是API25的源码,可以看出这就是一个小学的计算。。。)
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;
}
下表表示这些内插器为持续1000ms的动画计算的近似值: |
ms elapsed | (Linear) | Accelerate/Decelerate) |
0 | 0 | 0 |
200 | 0.2 | 0.1 |
400 | 0.4 | 0.345 |
600 | 0.6 | 0.8 |
800 | 0.8 | 0.9 |
1000 | 1.0 | 1.0 |
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);
有关如何使用
keyframe的更完整的示例,请参阅APIDemos中的 MultiPropertyAnimation示例。
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
ViewPropertyAnimator提供了一种简单的方式来使用单个底层的Animator对象并行地对视图的多个属性进行动画化。 它的行为非常类似于ObjectAnimator,因为它会修改视图属性的实际值,但在一次动画化多个属性时效率比ObjectAnimatior更高。另外,使用ViewPropertyAnimator的代码更简洁易读。以下代码展示了使用多个 ObjectAnimator objects,单个ObjectAnimator,以及the ViewPropertyAnimator之间的差异当同时动画化视图的x和y属性。
Multiple ObjectAnimator objectsObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();
One ObjectAnimator
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
ViewPropertyAnimatormyView.animate().x(50f).y(100f);
(编者按:下面是相关的源码)
/**
* This method returns a ViewPropertyAnimator object, which can be used to animate
* specific properties on this View.
*
* @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View.
*/
public ViewPropertyAnimator animate() {
if (mAnimator == null) {
mAnimator = new ViewPropertyAnimator(this);
}
return mAnimator;
}
/**
* This method will cause the View's x
property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator x(float value) {
animateProperty(X, value);
return this;
}
/**
* This method will cause the View's y
property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator y(float value) {
animateProperty(Y, value);
return this;
}
(编者按:不得不说实在简洁高效啊)
为了运行此动画,您必须将代码中的XML资源
inflate到AnimatorSet对象,然后在开始动画集之前设置所有动画的目标对象。调用setTarget()为AnimatorSet的所有子项设置单个目标对象。 以下代码显示如何执行此操作:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.anim.property_animator);
set.setTarget(myObject);
set.start();
你还可以在XML中声明ValueAnimator,如以下示例所示:
要在你的代码中使用上面的ValueAnimator,你必须
inflate 一个对象,添加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();
有关定义属性动画的XML语法的信息,请参阅 Animation Resources 。