Swing - Thread.sleep() 停止 JTextField.setText() 工作[重复]

Posted

技术标签:

【中文标题】Swing - Thread.sleep() 停止 JTextField.setText() 工作[重复]【英文标题】:Swing - Thread.sleep() stop JTextField.setText() working [duplicate] 【发布时间】:2012-12-15 22:17:48 【问题描述】:

可能重复:using sleep() for a single thread

我在使用 Thread.sleep() 时遇到了 JTextField.setText() 的问题。这是我正在制作的基本计算器。当输入字段中的输入格式不正确时,我希望“输入错误”在输出字段中出现 5 秒钟,然后将其清除。 setText() 方法 did 当我将文本设置一次为“INPUT ERROR”并通过打印中间的文本时发现它确实适用于该方法和 setText("") 之一在另一个之后。当我将 Thread.sleep() 放在它们之间时,问题就出现了。 这是代码的 SSCCE 版本:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.regex.Pattern;
import javax.swing.*;

public class Calc 
    static Calc calc = new Calc();

    public static void main(String args[]) 
        GUI gui = calc.new GUI();
    

    public class GUI implements ActionListener 

        private JButton equals;

        private JTextField inputField, outputField;

        public GUI() 
            createFrame();
        

        public void createFrame() 
            JFrame baseFrame = new JFrame("Calculator");
            baseFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JPanel contentPane = new JPanel();
            BoxLayout layout = new BoxLayout(contentPane, BoxLayout.Y_AXIS);
            contentPane.setLayout(layout);
            baseFrame.setContentPane(contentPane);
            baseFrame.setSize(320, 100);

            equals = new JButton("=");
            equals.addActionListener(this);

            inputField = new JTextField(16);
            inputField.setHorizontalAlignment(JTextField.TRAILING);
            outputField = new JTextField(16);
            outputField.setHorizontalAlignment(JTextField.TRAILING);
            outputField.setEditable(false);

            contentPane.add(inputField);
            contentPane.add(outputField);
            contentPane.add(equals);

            contentPane.getRootPane().setDefaultButton(equals);
            baseFrame.setResizable(false);
            baseFrame.setLocation(100, 100);

            baseFrame.setVisible(true);
        

        /**
         * When an action event takes place, the source is identified and the
         * appropriate action is taken.
         */

        @Override
        public void actionPerformed(ActionEvent e) 
            if (e.getSource() == equals) 
                inputField.setText(inputField.getText().replaceAll("\\s", ""));
                String text = inputField.getText();
                System.out.println(text);
                Pattern equationPattern = Pattern.compile("[\\d(][\\d-+*/()]+[)\\d]");
                boolean match = equationPattern.matcher(text).matches();
                System.out.println(match);
                if (match) 
                    // Another class calculates
                 else 
                    try 
                        outputField.setText("INPUT ERROR"); // This doesn't appear
                        Thread.sleep(5000);
                        outputField.setText("");
                     catch (InterruptedException e1) 
                    
                
            
        
    

我实际上并没有使用嵌套类,但我希望它能够为您包含在一个类中。对 GUI 的外观感到抱歉,但这又是为了减少代码。重要部分 (if (e.getSource() == equals)) 与我的代码保持不变。给出错误输入的最简单方法是使用字母。

【问题讨论】:

尝试将System.out.println() 放在那里,看看它是否能达到这一点 @Doorknob 我已经在自己的代码中做到了这一点,它正在达到这一点。 【参考方案1】:

当你使用Thread.sleep() 时,你是在主线程上做的。这会将 gui 冻结五秒钟,然后更新outputField。发生这种情况时,它会使用最后设置的空白文本。

使用Swing Timers 会更好,这里有一个示例可以完成您想要完成的工作:

if (match) 
    // Another class calculates
 else 
    outputField.setText("INPUT ERROR");
    ActionListener listener = new ActionListener()
        public void actionPerformed(ActionEvent event)
            outputField.setText("");
        
    ;
    Timer timer = new Timer(5000, listener);
    timer.setRepeats(false);
    timer.start();

【讨论】:

TimerTask 中使用invokeLater(),或者更好的是javax.swing.Timer @trashgod 好主意,我相应地更改了代码 在我的完整代码上完美运行,谢谢@aly 我认为这将添加一个新的循环计时器,该计时器将针对每个输入错误以 5000 毫秒的周期永远运行。 @msandiford 你是对的!再次修复【参考方案2】:

您正在 Swing 主线程中执行Thread.sleep()。这不是好的做法。您最多需要使用SwingWorker 线程。

发生的事情是它运行第一行,点击Thread.sleep()

这可以防止(主)EDT 线程进行任何重绘(以及防止下一行执行)。

您应该使用javax.swing.Timer 来设置延迟反应,而不是将sleep() 调用放在主线程中。

【讨论】:

你说得对,这里不应该使用线程睡眠,但我不认为睡眠被中断了。 1+ 这是它不会继续执行第二组文本的唯一原因。目前尚不清楚实际失败的结果是什么。无论如何,它完全有可能被中断,从而产生错误。 如果你修改他的代码并打印出 InterruptedException 的堆栈跟踪,你会发现它永远不会发生。相反,outputField.setText("INPUT ERROR"); 实际上确实被调用,Thread.sleep(5000); 被调用,然后在 5 秒延迟后 outputField.setText("");,但是您永远不会在 JTextField 中看到“输入错误”,因为 Thread.sleep(...) 放置了 Swing 事件调度线程或EDT 进入休眠状态,阻止它执行任何操作,包括绘制包含文本字段内容的 GUI。 有道理。更新了答案。 感谢你们两位的解释,我现在明白为什么会这样了——我对线程的理解有限,但这有帮助!【参考方案3】:

正如 Philip Whitehouse 在他的回答中所说,您正在使用 Thread.sleep(...) 调用阻塞摇摆事件调度线程。

鉴于您已经花时间设置了ActionListener,使用javax.swing.Timer 来控制清除文本可能是最简单的。为此,您可以在 GUI 类中添加一个字段:

    private Timer clearTimer = new Timer(5000, this);

GUI 的构造函数中,关闭重复功能,因为你真的只需要一次:

    public GUI() 
        clearTimer.setRepeats(false);
        createFrame();
    

然后,actionPerformed 可以修改为使用它来启动计时器/清除字段:

    public void actionPerformed(ActionEvent e) 
        if (e.getSource() == equals) 
            inputField.setText(inputField.getText().replaceAll("\\s", ""));
            String text = inputField.getText();
            System.out.println(text);
            Pattern equationPattern = Pattern.compile("[\\d(][\\d-+*/()]+[)\\d]");
            boolean match = equationPattern.matcher(text).matches();
            System.out.println(match);
            if (match) 
                // Another class calculates
             else 
                clearTimer.restart();
                outputField.setText("INPUT ERROR"); // This doesn't appear
            
         else if (e.getSource() == clearTimer) 
            outputField.setText("");
        
    

【讨论】:

您推荐使用 Swing Timer 是正确的,但我的偏好是不要要求 ActionListener 做太多事情,因为调试和维护可能会很麻烦。为什么不直接为侦听器使用匿名内部类呢? 1+ 为您提供好的建议。 此解决方案适用于我的示例,但不适用于我的原始代码(它没有消失),也许@HovercraftFullOfEels 的观点是正确的,因为在更复杂的代码问题出现。 aly 的解决方案确实有效。 @PaddyProgrammer:不,如果正确实施,它可以在您的原始代码中正常工作。你的问题无疑在于你执行他的建议。请注意,每当您在此处获得答案中的代码时,该代码绝不意味着您的问题的解决方案,而是示例代码,您可以将其想法用于您最终使用这些想法创建自己的代码解决方案。 @HovercraftFullOfEels 我理解,虽然我不只是放弃解决方案,但我可能没有正确实施它,但根据你所说的,它可能不是最好的解决方案。 FWIW,我同意 Hovercraft 先生所说的代码可以使用一些重组/应用最佳实践。我的目的是提供一个需要最少代码更改的正确解决方案。

以上是关于Swing - Thread.sleep() 停止 JTextField.setText() 工作[重复]的主要内容,如果未能解决你的问题,请参考以下文章

java thread.sleep 也让 swing ui 进入睡眠状态

java thread.sleep 也让 swing ui 进入睡眠状态

为啥 thread.sleep 在第一次捕获时不停止?

Windows 10上的Java Thread.sleep()在S3睡眠状态下停止

当 Java 中有 Thread.sleep 时,计时器不会停止

Thread.sleep 是如何真正工作的?