0

I have a game where I want to flash a component a red color, and then back to its normal color after a short delay. Obviously, I could just do Thread.sleep(123), but that is EXTREMELY undesirable because it breaks stuff on my UI. Plus, I am pretty sure that is not what I am supposed to do here.

Now, I could just do the pause off the queue, and then use SwingUtilities::invokeLater. But I wanted to ask to learn the best practices here.

What is the best way to add a specific length pause in between Swing/AWT Events?

Here is a runnable example.

import javax.swing.*;
import java.awt.*;

public class cry {

    public static void main(final String[] args) {

        SwingUtilities.invokeLater(cry::blah);

    }

    private static void blah() {
        final JFrame frame = new JFrame();
        frame.setTitle("ABC");
        frame.setLocationByPlatform(true);
        frame.setSize(300, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);

        try {
            Thread.sleep(10_000); //sleep for 10 seconds
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        
        frame.setTitle("XYZ");
    }

}
4
  • 2
    Let a javax.swing.Timer manage the waiting, for example.
    – trashgod
    Commented Jul 6 at 21:56
  • See also related examples here and here.
    – trashgod
    Commented Jul 6 at 22:15
  • @trashgod Thank you for the response. Side question then -- Swing Timers repeat or only execute once, so that would mean I would have to stop it once I have executed both commandA and commandB. My follow up question is, if my commandA or commandB take a really long time, then how do I ensure that the time only executes a specific number of times? I just learned that I can coalesce events, so it sounds like I would use some counter + a Lock to make sure that things only occur a set # of times? I know this is different than my original question, just exploring the concept is all. Commented Jul 6 at 22:29
  • Note that all instances of Timer "perform their waiting using a single, shared thread," so multiple timers are feasible; now that you know what to look for, please edit your question to include a minimal reproducible example that shows your revised approach.
    – trashgod
    Commented Jul 7 at 12:33

1 Answer 1

0

To achieve your goal, you can use the Timer class of Swing:

timer = new Timer( 1500, e -> {
   panel.setBackground( Color.darkGray );
   frame.setTitle( "XYZ" );
   timer.stop();
} );
timer.setInitialDelay( 3500 );
timer.setRepeats( false );
timer.start();

As you can see, the implementation is very simple, first we instantiate the Timer:

timer = new Timer(

as parameters we pass it the time between repetitions (although in this case they are not used):

1500,

and a lamba (abstract class) that will execute the code:

e -> {

inside it, we place the code to execute, and as we don't need repetitions, we call timer.stop(), to stop its execution.

   panel.setBackground( Color.darkGray );
   frame.setTitle( "XYZ" );
   timer.stop();
} );

All that's left is to finish setting the timer, and start it. We indicate the time from the beginning to the first execution:

timer.setInitialDelay( 3500 );

We indicate that we do not want repetitions:

timer.setRepeats( false );

We start it:

timer.start();

import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;  // not "java.util.Timer"

public class cry {
   Timer timer;
   JPanel panel;
   JFrame frame;

   public static void main( String[] args ) {
      Cry cry = new Cry();
      SwingUtilities.invokeLater( cry :: blah );
   }

   private void blah() {
      frame = new JFrame();
      frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
      frame.setLocationByPlatform( true );
      frame.setSize( 300, 300 );
      frame.setTitle( "ABC" );
      panel = new JPanel();
      panel.setBackground( Color.red );
      panel.setSize( 300, 300 );
      frame.add( panel );
      frame.setVisible( true );
      timer = new Timer( 1500, e -> {
         panel.setBackground( Color.darkGray );
         frame.setTitle( "XYZ" );
         timer.stop();
      } );
      timer.setInitialDelay( 3500 );
      timer.setRepeats( false );
      timer.start();
   }
}

Note: you will have noticed that I separated the declaration of frame, putting this in the body of the class, and the instantiation inside blah, this is necessary, because the call to timer.stop(), is inside a lambda, that is to say inside an internal class, from that place, only the attributes of the parent class can be "seen".

3
  • This is a very confusing answer. Your solution makes an entire Timer for a single event. What about a third event? Am I supposed to make an entire Timer for that too? Commented Jul 7 at 3:37
  • @davidalayachew, you can put the Timer in a method, and call it with parameters (initialDelay, time, repeats), but if you already have a Timer running in your game, you can use the calls of this one to modify a counter, and avoid using another one, you can also create a new thread, and put the Thread.sleep(10_000); and the code to execute on it, this way it does not alter the execution of the main thread. Commented Jul 7 at 5:56
  • @davidalayachew Valid point. As mentioned you can use and reuse a single timer (and its associated thread) for scheduling multiple timed tasks instead of creating a new one every time. Check out the javadoc for Timer. Note that there's also ScheduledThreadPoolExecutor. You should not put the Thread.sleep(10_000) instruction on any thread if you are concerned about making good use of the threads...
    – masterxilo
    Commented Jul 13 at 14:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.