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 里判断一下。

知识体系总览

下面这张图,帮你理清动画监听的整体脉络:

动画监听器知识体系 View动画 (Animation) 属性动画 (Animator) AnimationListener 接口 onAnimationStart onAnimationEnd onAnimationRepeat AnimatorListener 接口 onAnimationStart onAnimationEnd onAnimationCancel onAnimationRepeat AnimatorListenerAdapter(简化实现)

实际项目中的最佳实践

说了这么多理论,来点实际的。我总结了几条经验:

  1. 动画开始前,先禁用交互——在 onAnimationStart 里设置 view.setEnabled(false),结束再恢复。
  2. 动画结束后,记得清理资源——比如移除监听器本身,防止内存泄漏。
  3. cancel 和 end 的区别要搞清楚——cancel 是主动中断,end 是正常结束。两者都会触发 onAnimationEnd
  4. 能用 Adapter 就别用接口——代码少写一半,可读性还更高。

一句话总结:动画监听器就是动画的「闹钟」,告诉你什么时候该做什么事。View动画用 AnimationListener,属性动画用 AnimatorListenerAdapter,别搞混了。

嗯,这一节的内容就这些。动画监听看似简单,但用不好真的会出 bug。你想想看,如果动画还没结束你就去操作 UI,那画面得多诡异?

公众号:蓝海资料掘金营,微信deep3321