1

I have a piece of code I'm working on, I prepared a simple example:

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Example extends JFrame implements ActionListener {
    public static void main(String[] args) {
        Example e = new Example();
        e.setVisible(true);
    }

    private JButton button;

    public Example() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(600, 400);
        this.setLayout(null);

        button = new JButton("button");
        button.setBounds(200, 150, 200, 50);
        button.addActionListener(this);
        this.add(button);
    }
    @Override
    public void actionPerformed(ActionEvent actionEvent) {
        button.setEnabled(false);
        
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

The pooint is, we have actionPerformed function, that has two actions: disables the button and runs sleep to imitate some work that i do.

My problem is that button is being disabled after actionPerformed ends, so in other words after this 5 seconds of sleep, and I cannot disable it earlier. I've tried to do something with it but so far changing button.setEnabled(false); to button.setEnabled(false); this.repaint(); this.validate(); does nothing.

My question is: how to do in swing the job, where I need first disable button, then do some time consuming stuff (sleep for example) and enable button when job is done?

David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
Kq11
  • 87
  • 1
  • 7
  • I'm curious. Did you try [searching](https://stackoverflow.com/search?q=%5Bswing%5D+thread+sleep) before you posted your question? Calling `Thread.sleep()` from _Swing_ code has been asked (and answered) quite a few times already. [This question](https://stackoverflow.com/questions/16498174/thread-sleep-in-actionperformed) looks very similar to yours. Do you agree? – Abra Dec 09 '20 at 11:13

2 Answers2

1

That's because you cant Thread.sleep() on the same thread your UI is being created on or else it will freeze the UI until the sleep is finished.

What you would want to do is probably use a Swing Timer or Swing Worker instead.

Some other points:

  1. Don't extend JFrame class unnecessarily
  2. Don't use a null/AbsoluteLayout rather use an appropriate LayoutManager
  3. Don't call setBounds() or setSize() on components, if you use a correct layout manager this will be handled for you
  4. Call JFrame#pack() before setting the frame to visible
  5. All Swing components should be called on the EDT via SwingUtilities.invokeLater
  6. I've also never been a fan of implementing the ActionListener on a class basis especially if you will have more than 1 component registering an ActionListener.

Here is an example of the changes made and using a Timer:

import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.event.ActionEvent;
import javax.swing.border.EmptyBorder;

public class Example {

    private JButton button;
    private Timer timer;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(Example::new);
    }

    public Example() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel();
        panel.setBorder(new EmptyBorder(20, 20, 20, 20));

        button = new JButton("button");

        button.addActionListener((ActionEvent e) -> {
            if (timer != null && timer.isRunning()) {
                return;
            }
            
            button.setEnabled(false);

            timer = new Timer(5000, (ActionEvent e1) -> {
                // TODO do something after sleeping for 5 seconds
                button.setEnabled(true);
                timer.stop();
            });
            timer.start();
        });

        panel.add(button, BorderLayout.CENTER);
        frame.add(panel);
        frame.pack();
        frame.setVisible(true);
    }
}
David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
0

@David Kroukamp Thanks a lot, that's what I was looking for. If anyone needed here is simple (but not perfect, see toDavid's answer) example with Worker

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Example extends JFrame implements ActionListener {
    public static void main(String[] args) {
        Example e = new Example();
        e.setVisible(true);
    }

    private JButton button;

    public Example() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(600, 400);
        this.setLayout(null);

        button = new JButton("button");
        button.setBounds(200, 150, 200, 50);
        button.addActionListener(this);
        this.add(button);
    }
    @Override
    public void actionPerformed(ActionEvent actionEvent) {
        button.setEnabled(false);
        startThread();
    }

    private void startThread() {

        SwingWorker sw1 = new SwingWorker() {
            @Override
            protected Object doInBackground() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            protected void done() {
                button.setEnabled(true);
            }
        };
        
        sw1.execute();
    }
}

Kq11
  • 87
  • 1
  • 7