You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将Build SDK从23升级到27后onAnimationEnd触发两次问题求助

解决SDK23升级到27后AnimationListener.onAnimationEnd重复调用的问题

我之前在SDK版本升级时也碰到过类似的动画回调异常问题,结合你的代码和场景,咱们来分析下根本原因和更彻底的解决方案:

可能的根本原因

1. Android 7.0+ 动画系统的行为变更

从API 24开始,Android对动画的替换逻辑做了优化。你代码中先调用fragmentToPopView.clearAnimation();再立刻启动新动画,在旧版本(SDK23)中这会直接终止旧动画并启动新的,但在SDK27里,clearAnimation()会触发动画的取消回调,而新动画结束后又会触发一次正常的onAnimationEnd,导致两次回调。

2. setFillAfter(true)的状态处理差异

当设置setFillAfter(true)时,动画结束后视图会保持最终状态。SDK23之后,系统对视图填充状态的检测逻辑有调整,可能在动画结束后,视图状态的变更被系统额外捕获,从而再次触发onAnimationEnd。

3. FragmentManager与动画的时序冲突

SDK27中popBackStackImmediate()的内部实现有更新,在动画执行过程中执行Fragment弹出操作,可能触发视图的重新布局,间接导致动画回调被重复调用。

彻底解决方案(替代临时的isAnimating标志)

方案1:优化动画替换逻辑

传统的startAnimation()本身会自动替换当前视图正在运行的动画,不需要手动调用clearAnimation()。你可以移除fragmentToPopView.clearAnimation();这行代码,直接启动新动画:

@Override
public void onBackPressed() {
    Animation slideOutRightAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_out_right);
    slideOutRightAnimation.setFillAfter(true);
    slideOutRightAnimation.setAnimationListener(new AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {
            // 不需要isAnimating标志
        }

        @Override
        public void onAnimationRepeat(Animation animation) {}

        @Override
        public void onAnimationEnd(Animation animation) {
            getSupportFragmentManager().popBackStackImmediate();
        }
    });
    // 移除clearAnimation调用
    fragmentToPopView.startAnimation(slideOutRightAnimation);
}

如果担心视图有未结束的动画,也可以加个判断再清理:

if (fragmentToPopView.getAnimation() != null && !fragmentToPopView.getAnimation().hasEnded()) {
    fragmentToPopView.clearAnimation();
}

方案2:改用ViewPropertyAnimator(推荐)

传统Animation类在API12后就逐渐被ViewPropertyAnimator取代,它的回调更稳定,适配新版本系统的动画逻辑:

@Override
public void onBackPressed() {
    int viewWidth = fragmentToPopView.getWidth();
    fragmentToPopView.animate()
            .translationX(viewWidth) // 对应slide_out_right的效果
            .setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime))
            .setListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {}

                @Override
                public void onAnimationEnd(Animator animation) {
                    getSupportFragmentManager().popBackStackImmediate();
                }

                @Override
                public void onAnimationCancel(Animator animation) {}

                @Override
                public void onAnimationRepeat(Animator animation) {}
            });
}

这种方式能从根本上避免传统Animation的回调异常,而且代码更简洁,还支持硬件加速。

方案3:调整Fragment弹出的时序

如果暂时不想替换动画方式,可以把弹出操作延迟到动画完全结束后执行,避免时序冲突:

@Override
public void onAnimationEnd(Animation animation) {
    fragmentToPopView.postDelayed(() -> {
        getSupportFragmentManager().popBackStackImmediate();
    }, 50); // 短延迟确保动画状态稳定
}

总结

临时的isAnimating标志能解决当前页面的问题,但如果其他模块也有类似的动画+Fragment操作逻辑,很可能会出现同样的问题。推荐统一改用ViewPropertyAnimator,或者优化动画替换逻辑,这样能彻底适配SDK27的动画系统变化,避免后续出现更多隐藏问题。

内容的提问来源于stack exchange,提问作者Chris Gunawardena

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:46:06