Leonide Leonide - 2 months ago 20
Java Question

How to pause program execution without blocking a Timer (Java Swing)?

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.

Answer

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 ();
        …
    }
}