Android Studio中AnimationDrawable帧数量异常及重复播放问题
老哥,你的问题核心是AnimationDrawable的帧一直在累积,没有被清空,再加上动画结束的判断逻辑有问题,才导致了看起来重复播放、帧数量越来越多的异常。咱们一步步拆解:
异常原因
帧持续累积,未被清空
你每次调用createAnimation时,都是直接往同一个AnimationDrawable对象里addFrame,从来没清空过之前的帧。比如第一次加了270帧,第二次又加270帧,总帧数就变成540(你打印的693/1242应该是不同的endFrameIdx导致的累加)。setOneShot(true)是让动画播放完所有已添加的帧就停止,但因为帧越来越多,每次播放的是所有累积的帧,你就误以为是重复播放了。动画结束的判断逻辑不可靠
用currentFrame == prevFrame来判断动画结束太草率了:- Handler是每500ms调用一次,可能刚好在动画播放过程中捕捉到同一帧(比如帧切换间隔是1ms,500ms内已经切换了500帧,但某次调用时刚好和上一帧相同);
- 动画真的结束后,
currentFrame会停在最后一帧,这时候prevFrame也是最后一帧,这时候才应该处理,但你的逻辑可能在动画还在播放时就误触发了createAnimation,导致额外追加帧。
重复设置ImageView的Drawable
每次playAnimationOnce都调用imageanim.setImageDrawable(animation),这不仅没必要,还可能打乱AnimationDrawable的内部状态,导致播放行为异常。
修复方案
1. 清空原有帧再添加新帧
在createAnimation开头先清空AnimationDrawable的所有帧。注意AnimationDrawable.clear()是API 28及以上才有的方法,如果你的项目兼容更低版本,得用反射或者遍历移除:
void createAnimation(AnimationDrawable animation, int endPixelIdx){ // 先清空已有帧 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { animation.clear(); } else { // 低版本反射移除所有帧 try { Field framesField = AnimationDrawable.class.getDeclaredField("mFrames"); framesField.setAccessible(true); ((ArrayList<?>) framesField.get(animation)).clear(); Field frameDurationsField = AnimationDrawable.class.getDeclaredField("mFrameDurations"); frameDurationsField.setAccessible(true); ((int[]) frameDurationsField.get(animation)) = new int[0]; } catch (Exception e) { e.printStackTrace(); } } int endFrameIdx = Math.min(endPixelIdx, 270); print("start animation, end frame index is " + endFrameIdx); for (int i = 0; i < endFrameIdx; i++) { animation.addFrame(artArr[i], 1); } }
2. 修正动画结束的判断逻辑
改用isRunning()来判断动画是否在播放,只有当动画已经停止时,才重新创建动画并播放:
void playAnimationOnce(int stopFrameIdx){ // 只有当动画没在播放时,才重新创建并启动 if (!animation.isRunning()) { createAnimation(animation, stopFrameIdx); animation.setOneShot(true); animation.start(); System.out.println("Number of frames to play: " + animation.getNumberOfFrames()); } // 移除原来的prevFrame判断逻辑,改用isRunning更准确 }
3. 优化ImageView的Drawable设置
只在初始化时设置一次,比如在onCreate里:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 初始化时设置一次即可 animation = new AnimationDrawable(); imageanim.setImageDrawable(animation); final Handler handler=new Handler(); handler.post(new Runnable(){ @Override public void run() { distTextView.setText(intDistance + " Inches"); int pixel = (int) (intDistance / inchPerPixel); playAnimationOnce(pixel); handler.postDelayed(this,500); } }); }
4. 避免频繁触发动画
如果你的信号是频繁传来的,建议加个判断:只有当stopFrameIdx和上一次的帧索引不同时,才重新创建动画,避免不必要的重复操作。
总结
核心就是每次创建新动画前一定要清空旧帧,再配合可靠的动画状态判断,就能解决帧累积和看似重复播放的问题了。
内容的提问来源于stack exchange,提问作者I Like

