Android动画:等待动画完成?

35

在Android ImageView中等待动画完成后才能继续程序执行,正确的方法是什么?

  • (在这种情况下,“完成”意味着它恰好运行所有帧一次,并停止在最后一个帧上。我不清楚这个动画是否是android:oneshot =“ true”动画,因为我将多次使用它,但它不会连续运行而是间歇性地运行)

研究/猜测:

A. 本质上,我的问题似乎是一个Java线程问题,因为Android AnimationDrawable 实现了Java.lang.Runnable。因此,也许线程是解决方案。也许答案会包括join

B. 其他人的方法似乎是使用AnimationListener,这对于我的简单需求来说似乎很困难和复杂。另外我不太确定该如何做。

C. AnimationDrawable类有一个(布尔值)isRunning方法,可以在while循环中使用(即while(anim.isRunning(){wait(100ms)})。但是我有一种直觉认为这是错误的方法。尽管类似的内容似乎在并发教程中提到过。

代码片段

   this.q_pic_view.setImageResource(0);
    this.q_pic_view.setBackgroundResource(R.drawable.animation_test);
    AnimationDrawable correct_animation = (AnimationDrawable) this.q_pic_view.getBackground();
    correct_animation.start();

    //here I tried to implement option C but it didn't work
    while(correct_animation.isRunning()){
        try {
           Thread.sleep(20);
        } catch (InterruptedException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
        }
    }

动画

<?xml version="1.0" encoding="utf-8"?>
<animation-list android:id="@+id/AnimTest" android:oneshot="true" xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@drawable/animtest001" android:duration="33"/>
  <item android:drawable="@drawable/animtest002" android:duration="100"/>
  <item android:drawable="@drawable/animtest003" android:duration="66"/>
  <item android:drawable="@drawable/animtest004" android:duration="66"/>
  <item android:drawable="@drawable/animtest005" android:duration="33"/>
  <item android:drawable="@drawable/animtest006" android:duration="66"/>
  <item android:drawable="@drawable/animtest007" android:duration="33"/>
  <item android:drawable="@drawable/animtest008" android:duration="66"/>
  <item android:drawable="@drawable/animtest009" android:duration="100"/>
  <item android:drawable="@drawable/animtest010" android:duration="100"/>
</animation-list>

实现所需效果的一种可能方式,虽然不是我问题的答案,但可以通过执行以下操作延迟进一步代码的执行:

int duration = 0;
//Add all of the frames together
for (int i=0; i<my_animation.getNumberOfFrames(); i++){
    duration = duration + correct_animation.getDuration(i);
    }
//delay the execution 
Handler handler = new Handler();
handler.postDelayed(new Runnable(){
    public void run() {
       DoYourNextStuff();
        }
}, duration); //delay is here

编辑:有很多方法可以解决这个问题,给出的答案可能确实解决了问题(我没有测试),但是我最终只是等待正确的时间(起初),然后改用异步任务(它具有完成处理程序方法)并直接在异步任务的updateProgress调用中运行我的动画。在最后一次迭代中,我使用了一个线程表面视图来运行动画。这种方式迄今为止是最快和最好的(除了本帖子所询问的原因之外)。我希望它能帮助到某些人。


动画需要多长时间才能完成? - Niranj Patel
CapDroid,如果你将上面的XML片段中的所有整数相加,你就可以得到动画的总持续时间。但我正在寻找一般性的解决方案,所以这在这种情况下并没有帮助我。 - tjb
只需尝试使用Thread.sleep(663); 663 = 动画的总时间。 - Niranj Patel
4个回答

58

最简单的方法是向UI线程发布一个(延迟)Runnable:

Animation fadeout = new AlphaAnimation(1.f, 0.f);
fadeout.setDuration(500);
view.startAnimation(fadeout);
view.postDelayed(new Runnable() {
    @Override
    public void run() {
        view.setVisibility(View.GONE);
    }
}, 500);

这样做可以轻松完成任务。而且在Android中千万不要(绝对不要,永远不要!)尝试阻塞UI线程。如果你这么做了,手机会冻结并且你也看不到动画。如果你需要等待一些时间,请使用另一个线程。


非常好的答案,但是当在run()中进行大量/数据库工作以及if动画已经结束的条件时,我遇到了一个小问题。animation.hasEnded()它可能不总是返回true。通过增加postDelayed中的延迟毫秒数,超过动画持续时间,解决了这个问题 :) - Wesley
这段代码真是太棒了,如此简洁而且运行得非常好!感谢您的发布!! - deucalion0
我真的不相信这是一个好的解决方案。硬编码数值很少是一个好的选择。 - katzenhut

50
使用Animation监听器来监听动画生命周期挂钩。
Animation fadeout = new AlphaAnimation(1.f, 0.f);
fadeout.setDuration(500);    
final View viewToAnimate = view;
fadeout.setAnimationListener(new AnimationListener(){

   @Override
   public void onAnimationStart(Animation animation){}

   @Override
   public void onAnimationRepeat(Animation animation){}

   @Override
   public void onAnimationEnd(Animation animation){
       viewToAnimate.setVisibility(View.GONE);
   }
});
view.startAnimation(fadeout);

3
我不知道为什么这不是被接受和点赞最多的答案。它显然是正确的。无论如何,我会给它一个赞。 - katzenhut
2
@katzenhut,因为现在的上下文中答案是错误的。OP正在使用AnimationDrawable,它甚至不派生自Animation。所以如果您正在使用从Animation类派生的动画,则此答案是正确的,但对于AnimationDrawable则不正确。与Animation类相比,AnimationDrawable在其生命周期方面没有回调函数。 - AgentKnopf
1
@Zainodis - Android中奇怪的命名规则总是让我惊叹不已...但你是正确的。感谢你的澄清! - katzenhut

9

建议您:

  • 创建一个对象来封装“动画”生命周期
  • 在对象中,您可以使用线程或定时器
  • 提供start()方法来启动动画和awaitCompletion()方法等待动画完成
  • 使用private final Object completionMonitor字段跟踪完成情况,在其上同步,并使用wait()notifyAll()协调awaitCompletion()

代码片段:

final class Animation {

    final Thread animator;

    public Animation()
    {
      animator = new Thread(new Runnable() {
        // logic to make animation happen
       });

    }

    public void startAnimation()
    {
      animator.start();
    }

    public void awaitCompletion() throws InterruptedException
    {
      animator.join();
    }
}

您可以使用单个线程的 ThreadPoolExecutorScheduledThreadPoolExecutor,并将动画的每一帧作为一个 Callable 进行捕获。将一系列的 Callable 提交,并使用 invokeAll()CompletionService 阻塞您感兴趣的线程,直到动画完成。

4

你可以考虑使用 ObjectAnimator 这个方法。和其他人提到的类似,你需要使用一个监听器。

//get an image resource    
ImageView imgView = (ImageView) findViewById(R.id.some_image);      
//create and init an ObjectAnimator  
ObjectAnimator animation;
animation = ObjectAnimator.ofFloat(imgView, "rotationY", 0.0f, 360f);

//set the duration of each iteration
animation.setDuration(1500);
//set number of iterations
animation.setRepeatCount(1);
//set setInterpolator 

animation.setInterpolator(new AccelerateDecelerateInterpolator());
//Add a listner to check when the animation ends
animation.addListener(new AnimatorListenerAdapter() {
       @Override
       public void onAnimationEnd(Animator animation) {
                //postFirstAnimation(): This function is called after the animation ends
                postFirstAnimation();
       }
});
//start the animation
animation.start();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接