》自Android 3.0版本开始,系统给我们提供了一种全新的动画模式,属性动画(property animation)。
Android之前的补间动画机制其实还算是比较健全的,在android.view.animation包下面有好多的类可以供我们操作,来完成一系列的动画效果,比如说对View进行移动、缩放、旋转和淡入淡出,并且我们还可以借助AnimationSet来将这些动画效果组合起来使用,除此之外还可以通过配置Interpolator来控制动画的播放速度等。
新引入的属性动画机制已经不再是针对于View来设计的了,也不限定于只能实现移动、缩放、旋转和淡入淡出这几种动画操作,同时也不再只是一种视觉上的动画效果了。它实际上是一种不断地对值进行操作的机制,并将值赋值到指定对象的指定属性上,可以是任意对象的任意属性。所以我们仍然可以将一个View进行移动或者缩放,但同时也可以对自定义View中的Point对象进行动画操作了。我们只需要告诉系统动画的运行时长,需要执行哪种类型的动画,以及动画的初始值和结束值,剩下的工作就可以全部交给系统去完成了。
既然属性动画的实现机制是通过对目标对象进行赋值并修改其属性来实现的,那么之前所说的按钮显示的问题也就不复存在了,如果我们通过属性动画来移动一个按钮,那么这个按钮就是真正的移动了,而不再是仅仅在另外一个位置绘制了而已。
》ValueAnimator、ObjectAnimator、AnimatorSet、组合动画、XML配置动画属性
1. 属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据你传递的该熟悉的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终值 。
private
void
performAnimate() {
ViewWrapper wrapper =
new
ViewWrapper(mButton);
ObjectAnimator.ofInt(wrapper, width,
500
).setDuration(
5000
).start();
}
@Override
public
void
onClick(View v) {
if
(v == mButton) {
performAnimate();
}
}
private
static
class
ViewWrapper {
private
View mTarget;
public
ViewWrapper(View target) {
mTarget = target;
}
public
int
getWidth() {
return
mTarget.getLayoutParams().width;
}
public
void
setWidth(
int
width) {
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
> 1.View动画(渐变动画)的功能是有限的,大家可以尝试使用属性动画
2.为了在各种安卓版本上使用属性动画,你需要采用nineoldandroids,它是GitHub开源项目,jar包和源码都可以在网上下到,如果下不到jar包,我可以发给大家
3.再复杂的动画都是简单动画的合理组合,再加上本文介绍的方法,可以对任何属性作用动画效果,也就是说你几乎可以做出任何动画
4.属性动画中的插值器(Interpolator)和估值器(TypeEvaluator)很重要,它是实现非匀速动画的重要手段,你应该试着搞懂它,最好你还能够自定义它们
private
void
performAnimate(
final
View target,
final
int
start,
final
int
end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(
1
,
100
);
valueAnimator.addUpdateListener(
new
AnimatorUpdateListener() {
//持有一个IntEvaluator对象,方便下面估值的时候使用
private
IntEvaluator mEvaluator =
new
IntEvaluator();
@Override
public
void
onAnimationUpdate(ValueAnimator animator) {
//获得当前动画的进度值,整型,1-100之间
int
currentValue = (Integer)animator.getAnimatedValue();
Log.d(TAG, current value: + currentValue);
//计算当前进度占整个动画过程的比例,浮点型,0-1之间
float
fraction = currentValue / 100f;
//这里我偷懒了,不过有现成的干吗不用呢
//直接调用整型估值器通过比例计算出宽度,然后再设给Button
target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
target.requestLayout();
}
});
valueAnimator.setDuration(
5000
).start();
}
@Override
public
void
onClick(View v) {
if
(v == mButton) {
performAnimate(mButton, mButton.getWidth(),
500
);
}
}
Animator.start()
// start the animation from the beginning
Animator.end()
// end the animation
Animator.cancel()
// cancel the animation
Animator.pause()
// added in API 19; pause the animation
Animator.resume()
// added in API 19; resume a paused animation
>有些时候,我们需要去查询当前动画的状态,这个需求可以通过下面这些方法来完成。
1
2
3
|
boolean
isStarted()
// added in API 14
boolean
isRunning()
boolean
isPaused()
// added in API 19
|
如果当前动画已经调用了start函数并且还没播放完成也没有被取消掉,那么isStarted方法会返回true。请注意,isStarted 方法最低的 API 需求是 14.同时,就算是在动画播放延迟中,该方法依然会返回 true。这就是这个方法和 isRunning 方法的不同点,isRunning 方法只会在动画确实在播放并且还没停止的时候返回 true。
在 API 19 的时候,isPaused 方法被加入进来。这是由于那时候动画可以被暂停和恢复了,如果 isPaused 返回了 true,那么说明当前动画是在暂停状态下,反之亦然。
当我们通过pause调用去暂停动画时,isPaused会返回true。然后通过 resume调用去恢复动画播放后,isPaused也会变成false。可以注意到,图中如果动画是自然结束的,动画的状态并没有改变。当然,动画停止播放后,isStarted和isRunning肯定应该是返回false的。实际上,在动画自然停止后,如果我们再去调用 isStarted和isRunning他们的的确确会返回false。