TerraNova993
TerraNova993

Reputation: 53

strange bug - how to pause a java program?

I'm trying to:

  1. display a text in a jLabel,
  2. wait for two seconds,
  3. then write a new text in the jLabel

this should be simple, but I get a strange bug: the first text is never written, the application just waits for 2 seconds and then displays the final text. here is the example code:


private void testButtonActionPerformed(java.awt.event.ActionEvent evt) {    
    displayLabel.setText("Clicked!");

    //  first method with System timer

    /*
    long t0=  System.currentTimeMillis();
    long t1=  System.currentTimeMillis();
            do{
                t1 = System.currentTimeMillis();
            }
            while ((t1 - t0) < (2000));
     */    

    // second method with thread.sleep()

    try {
        Thread.currentThread().sleep(2000);
    } catch (InterruptedException e) {}

    displayLabel.setText("STOP"); 
}

with this code, the text "Clicked!" is never displayed. I just get a 2 seconds - pause and then the "STOP" text. I tried to use System timer with a loop, or Thread.sleep(), but both methods give the same result.

Upvotes: 2

Views: 1095

Answers (3)

ring bearer
ring bearer

Reputation: 20783

You are messing with the event dispatcher thread. That will cause un-expected UI behavior as you are seeing. If you plan to do these type of animations, make sure to read up on what @Andrew Thompson suggested and also, see if you can read this - Filthy rich clients

Better to use a Swing Timer as shown in curde-example below:(yes, it is crude, I did not worry about stopping the timer etc):

public class DelayTest  extends JPanel{
    JLabel messageLabel = new JLabel();
    JButton actionButton = new JButton("Click Me");
    String[] messages = {"Clicked", "Stop!"};
    int i=0;
    public DelayTest(){
        super();
        add(messageLabel);
        add(actionButton);
        actionButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent arg0) {
                Timer timer = new Timer(1000, new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent arg0) {
                        if(i<=1)
                            messageLabel.setText(messages[i++]);
                    }
                });
                timer.start();

            }

        });
    }

}

Edit
Why not stop the Timer:

           @Override
           public void actionPerformed(ActionEvent evt) {
              if (i <= 1) {
                 messageLabel.setText(messages[i++]);
              } else {
                 ((Timer)evt.getSource()).stop();
              }
           }
        });

Upvotes: 2

ulmangt
ulmangt

Reputation: 5423

Here's a runnable example which does what you're after. As Andrew Thompson's comment stated, a SwingWorker is a good way to approach this problem.

The basic principal is to never block the Event Dispatch Thread. That's the thread responsible for repainting the GUI and responding to user interaction, so if you do something computationally expensive on the EDT, your GUI will stop responding.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.ExecutionException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingWorker;

public class ButtonTest {
    public static void main(String[] args) {

        // create a frame and a button
        JFrame frame = new JFrame();
        final JButton button = new JButton("Button");
        frame.add(button);

        // add an action listener to the button
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent arg0) {

                // change the button text right away
                button.setText( "Clicked" );

                // create a SwingWorker which simply waits 2000 milliseconds
                // simulating a computation being performed
                SwingWorker<String, Object> worker = new SwingWorker<String, Object>() {
                    @Override
                    public String doInBackground() {

                        // it's safe to call Thread.sleep( ) here
                        // doInBackground is executed on a separate worker
                        // thread
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                        }

                        return "Done";
                    }

                    @Override
                    protected void done() {

                        // done() is executed back on the Swing thread
                        // so it's safe to updated the state of the button
                        try {
                            button.setText(get());
                        } catch (Exception e) { }
                    }
                };

                // run the worker
                worker.execute();
            }
        });

        frame.setSize( 300, 300 );
        frame.setVisible( true );
    }
}

Upvotes: 2

Thomas
Thomas

Reputation: 88727

Just to provide more background on Andrew Thompson's comment: the EDT is responsible for handling gui updates. If you block it using Thread.sleep(...) those updates are blocked as well. That's why you don't see the first text - the EDT just can't do the update on the label.

Upvotes: 2

Related Questions