5、View动画之动画监听器:AnimationListener接口、AnimatorListenerAdapter的使用、动画状态回调
动画做完了,怎么知道它什么时候开始、什么时候结束?
这个问题,我刚入行那会儿还真吃过亏。有一次做一个登录按钮的动画,动画播放完需要跳转页面,结果我直接在动画后面写了跳转代码。你猜怎么着?页面跳转和动画同时发生了,用户体验极差。后来我才知道,原来动画是异步执行的,你得监听它的状态才行。
AnimationListener接口:View动画的监听方式
在View动画体系里,监听动画状态靠的是 Animation.AnimationListener 接口。这个接口定义了三个回调方法:
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
// 动画开始
}
@Override
public void onAnimationEnd(Animation animation) {
// 动画结束
}
@Override
public void onAnimationRepeat(Animation animation) {
// 动画重复
}
});
这三个方法,说白了就是告诉你动画的「生命周期」。
| 回调方法 | 触发时机 | 常见用途 |
|---|---|---|
| onAnimationStart | 动画开始播放时 | 显示加载状态、禁用按钮 |
| onAnimationEnd | 动画播放完毕时 | 跳转页面、恢复状态、触发后续操作 |
| onAnimationRepeat | 动画重复播放时 | 更新计数器、改变颜色 |
我个人习惯,在 onAnimationStart 里先禁用用户交互,防止动画过程中用户乱点。等 onAnimationEnd 再恢复,这样体验会好很多。
核心要点:View动画的监听器是接口,三个方法必须全部实现。哪怕你只关心结束回调,也得把另外两个方法写上,哪怕里面是空的。
AnimatorListenerAdapter:属性动画的偷懒神器
到了属性动画(Animator)时代,监听方式升级了。但问题也来了——Animator.AnimatorListener 接口有四个方法:
public interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
每次都要实现四个方法,烦不烦?
Google 也想到了这一点,所以提供了 AnimatorListenerAdapter。它是一个抽象类,帮我们把所有方法都实现了空实现。你只需要重写自己关心的那个方法就行。
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// 我只关心结束回调
// 其他方法不用管,Adapter已经帮我写好了空实现
}
});
我在项目中遇到过好几次,新人直接用匿名内部类实现 Animator.AnimatorListener,四个方法全写上,里面三个是空的。我就跟他们说:「用 AnimatorListenerAdapter 啊,省事多了。」
小技巧:如果你用的是 Kotlin,还可以用 addListener 的扩展函数,只传一个 lambda 就行。不过那是后话了,咱们先把 Java 版本吃透。
动画状态回调:不只是开始和结束
除了开始和结束,属性动画还多了个 onAnimationCancel 回调。这个回调什么时候触发?
当你调用 animator.cancel() 时,就会触发 onAnimationCancel。注意,cancel() 之后 onAnimationEnd 也会被调用。所以如果你在 onAnimationEnd 里做了某些操作,要小心别被 cancel 误触。
我曾经踩过这个坑:
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// 这里做了页面跳转
startActivity(intent);
}
@Override
public void onAnimationCancel(Animator animation) {
// 用户取消了动画
// 但 onAnimationEnd 也会被调用,导致页面还是跳转了
}
});
解决办法很简单:加一个标志位,或者在 onAnimationCancel 里重置状态。
注意:cancel() 会同时触发 onAnimationCancel 和 onAnimationEnd。如果你只想在正常结束时做操作,记得在 onAnimationCancel 里设置一个标志位,然后在 onAnimationEnd 里判断一下。
知识体系总览
下面这张图,帮你理清动画监听的整体脉络:
实际项目中的最佳实践
说了这么多理论,来点实际的。我总结了几条经验:
- 动画开始前,先禁用交互——在
onAnimationStart里设置view.setEnabled(false),结束再恢复。 - 动画结束后,记得清理资源——比如移除监听器本身,防止内存泄漏。
- cancel 和 end 的区别要搞清楚——cancel 是主动中断,end 是正常结束。两者都会触发
onAnimationEnd。 - 能用 Adapter 就别用接口——代码少写一半,可读性还更高。
一句话总结:动画监听器就是动画的「闹钟」,告诉你什么时候该做什么事。View动画用 AnimationListener,属性动画用 AnimatorListenerAdapter,别搞混了。
嗯,这一节的内容就这些。动画监听看似简单,但用不好真的会出 bug。你想想看,如果动画还没结束你就去操作 UI,那画面得多诡异?