Swing:单击时禁用按钮

Swing: make button disabled on click

我有一段代码正在处理,我准备了一个简单的例子:

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

要点是,我们有 actionPerformed 函数,它有两个操作:禁用按钮并运行 sleep 来模仿我所做的一些工作。

我的问题是按钮在 actionPerformed 结束后被禁用,换句话说,在这 5 秒的睡眠之后,我无法提前禁用它。我试图用它做点什么,但到目前为止,将 button.setEnabled(false); 更改为 button.setEnabled(false); this.repaint(); this.validate(); 什么也没做。

我的问题是:如何在 swing 工作中做,我需要先禁用按钮,然后做一些耗时的事情(例如睡眠)并在工作完成后启用按钮?

那是因为您不能 Thread.sleep() 在创建 UI 的同一线程上,否则它会冻结 UI 直到 sleep完成。

您想要做的可能是使用 Swing Timer or Swing Worker

其他几点:

  1. 不要不必要地扩展 JFrame class
  2. 不要使用 null/AbsoluteLayout 而是使用适当的 LayoutManager
  3. 不要在组件上调用 setBounds()setSize(),如果您使用正确的布局管理器,系统会为您处理此问题
  4. 在将框架设置为可见之前调用 JFrame#pack()
  5. 应通过 SwingUtilities.invokeLater
  6. EDT 上调用所有 Swing 组件
  7. 我也从来不喜欢在 class 的基础上实现 ActionListener,尤其是当你有超过 1 个组件注册 ActionListener 时。

这里是使用 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 非常感谢,这就是我一直在寻找的。如果这里需要的人很简单(但不完美,请参阅 toDavid 的回答)示例 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();
    }
}