1

I have a Timer that shows an animation on the screen when a game event takes place. What I want to do is have the main execution pause until this animation finishes, but everything I try seems to block the Timer from running. I've tried using Thread.sleep() and calling wait() and notify() on a lock object, but with the same result. The Timer listener's actionPerformed() is never called.

This code sets up the timer:

protected void showMovingEffect(int steps, Direction dir, AnimatedImageSet imgs) {
    effectsPane.runAnimation(imgs, dir, steps);
    waiting = true;
    while (waiting) {
        synchronized(animationWaitLock) {
            try {
                animationWaitLock.wait();
            } catch (InterruptedException e) {}
        }
    }
}

And the Timer and listener inside the EffectsPane object:

private void runAnimation(AnimatedImageSet ais, Direction dir, int iters) {
        System.out.println("run");
        imgs = ais;
        top = 0;
        left = 0;
        topStep = dir.getRowIncrement() * 10;
        leftStep = dir.getColIncrement() * 10;
        iterations = iters;
        index = 0;
        active = true;
        timer.start();
    }

public void actionPerformed(ActionEvent e) {
        System.out.println(index);
        top += topStep;
        left += leftStep;
        index++;
        currentImage = imgs.getImage(index);
        repaint();
        if (index == iterations) { 
            active = false;
            timer.stop();
            synchronized(animationWaitLock) {
                animationWaitLock.notify();
            }
            waiting = false;
        }
    }

The System.out.println call at the start of the actionPerformed() is never called, so either the wait() call is also pausing the Timer, or something is blocking it. If I comment out the sleep/wait lines in showMovingEffect(), the animation runs, but the program does not pause.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Leonide
  • 235
  • 3
  • 11

2 Answers2

3

One approach would be to display a modal dialog while the animation proceeds. As discussed here, user interaction will be foreclosed, but background GUI updates in response to the javax.swing.Timer will continue. You can allow the user to dismiss the dialog at any time, as shown here, but you may want to abandon the animation at that point.

no input from the user is needed.

You can block user interaction without displaying a dialog by entering a SecondaryLoop after the animation starts.

SecondaryLoop loop = Toolkit.getDefaultToolkit()
    .getSystemEventQueue().createSecondaryLoop();
timer.start();
loop.enter();

When the animation concludes, exit() the SecondaryLoop:

public void actionPerformed(ActionEvent e) {
    …
    if (index == iterations) {
        timer.stop();
        loop.exit ();
        …
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • This isn't what I'm looking for, I'm afraid. I'm looking for a way to pause after starting the timer, and restart when it finishes. Having a dialog box would serve no purpose, as no input from the user is needed. – Leonide Sep 09 '16 at 06:35
  • @Leonide: I'm wary of blocking user interaction with no visual feedback or escape mechanism; but for a short animation, you can use a `SecondaryLoop` as outlined above. – trashgod Sep 09 '16 at 10:02
  • Possibly tangential question. The first thing I tried with this was to have the runAnimation method have a loop that periodically refreshed the effectsPane, but with this, it was pause like I wanted, but the effectsPane wouldn't refresh. Any idea why? – Leonide Sep 09 '16 at 17:07
  • @Leonide: At a guess, the loop blocked the existing event queue; a `SecondaryLoop` blocks user interaction but lets the animation proceed. – trashgod Sep 09 '16 at 22:08
  • +++ 1mio for SecondaryLoop :-), [now is searchable here](http://stackoverflow.com/search?tab=newest&q=%5bswing%5d%20SecondaryLoop), there, everywhere – mKorbel Sep 10 '16 at 10:36
0

You can try this:

timer.addActionListener(new ActionListener() {

    @Override
    public void actionPerformed(ActionEvent e) {
        // your code
    }
});
Numb
  • 155
  • 3
  • How does this help? – ChiefTwoPencils Sep 09 '16 at 03:12
  • because on this code, this actionPerformed is your method, not acction listener method. so if you want to run it, you must call `effectsPane.actionPerformed(java.awt.Event);` like `effectsPane.runAnimation(imgs, dir, steps);` or add @Override to your actionPerformed method – Numb Sep 09 '16 at 03:18
  • Tried this, but with the same result. The actionPerformed method still isn't getting called. Does it matter whether the Timer is a member of the class calling showMovingEffect or the JPanel showing the animation? – Leonide Sep 09 '16 at 06:41