【问题标题】:JFrame not updating in real timeJFrame 没有实时更新
【发布时间】:2016-01-10 02:31:09
【问题描述】:

我的项目包含两个部分:一个逻辑模块和一个 gui 界面。 两者都将他们的引用发送给其他人。

当用户发送消息时,我有一个 Key Listener。在这个监听器中,我在逻辑之前调用相同的 gui 更改,在逻辑之后调用相同的更改。 问题是这两个更改将在执行结束时同时显示。

如何强制实时更新GUI?

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

public class Main {

    // Init DecisionEngine and GUIApplication
    private static final Logic _logic = new Logic();
    private static final GUI _gui = new GUI();

    public static void main(String[] args) {
        // Set DecisionEngine reference in GUIApplication and viceversa
        _gui.setLogicReference(_logic);
        _logic.setGUIRefecence(_gui);

        // User send a message
        _gui.textInput.addKeyListener(new KeyListener(){
            @Override public void keyPressed(KeyEvent e){
                if (e.getKeyCode() == KeyEvent.VK_ENTER) {

                    _gui.somethingChaged1();// CHANGE 1 GUI
                    _logic.thinking();// LOGIC PROCESSING (3sec)
                    _gui.somethingChaged2();// CHANGE 2 GUI

                    e.consume();// Stopping adding an Enter after message
                }
            }
            @Override public void keyTyped(KeyEvent e) {}
            @Override public void keyReleased(KeyEvent e) {}
        });
    }

    private static class Logic {
        GUI gui_ref;

        public Logic() {}
        private void setGUIRefecence(GUI _gui) {gui_ref = _gui;}
        private void thinking() {
            try {Thread.sleep(3000);} catch (InterruptedException ex) {}
        }
    }

    private static class GUI {

        Logic logic_ref;
        private JFrame frame;
        public  JTextArea textInput;
        private JLabel isTyping;

        public GUI() {

            frame = new JFrame();
            textInput = new javax.swing.JTextArea(5, 20);
            isTyping = new JLabel("Normal mode");

            frame.setSize(new Dimension(200,300));
            frame.add(textInput, BorderLayout.PAGE_START);
            frame.add(isTyping, BorderLayout.CENTER);
            frame.revalidate();
            frame.repaint();
            frame.setVisible(true);
        }

        private void setLogicReference(Logic _logic) {logic_ref = _logic;}
        private void somethingChaged1() {isTyping.setText("loading...");System.out.println("status changed in 'loading...'");}
        private void somethingChaged2() {isTyping.setText("is done.");System.out.println("status changed in 'is done.'");}
    }

}

【问题讨论】:

  • 发布MCVE。请务必将您的代码复制粘贴到新项目,并确保在将其发布到此处之前编译并运行。
  • private static final logic _logic = new Logic(); 不会编译。此外,不要在 Java 中的变量名中使用下划线。
  • @Adrian user1803551 完全正确,因为你的代码中第一行的大小写不一致,这不会编译。据我所知,您的程序有效;这是你的全部代码吗?
  • 作为一般规则,避免使用KeyListener,而是使用Key Bindings API。听起来你几乎屏蔽了 EDT
  • try {Thread.sleep(3000);} catch (InterruptedException ex) {} ..你想知道为什么它会冻结?!? @MadProgrammer 是对的。不要阻塞 EDT(事件调度线程)。发生这种情况时,GUI 将“冻结”。有关详细信息和修复,请参阅 Concurrency in Swing

标签: java multithreading swing


【解决方案1】:

为了澄清我在markbernard's answer中的评论,应该这样做:

    @Override public void keyPressed(KeyEvent e){
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
            // Change the GUI from the EDT (current thread):
            _gui.somethingChaged1();

            // start a new Thread to do the long processing:
            Thread t = new Thread(new Runnable() {
              public void run() {
                _logic.thinking();// LOGIC PROCESSING (3sec)

                // when the processing in this new Thread is complete,
                // update the GUI again via the EDT:
                SwingUtilities.invokeLater(new Runnable(){
                  public void run() {
                    _gui.somethingChaged2();
                  }
                });
              }
            }, "Logic Code");
            t.start();

            e.consume();// Stopping adding an Enter after message
        }
    }

【讨论】:

    【解决方案2】:

    你的问题就在这里:

            @Override public void keyPressed(KeyEvent e){
                if (e.getKeyCode() == KeyEvent.VK_ENTER) {
    
                    _gui.somethingChaged1();// CHANGE 1 GUI
                    _logic.thinking();// LOGIC PROCESSING (3sec)
                    _gui.somethingChaged2();// CHANGE 2 GUI
    
                    e.consume();// Stopping adding an Enter after message
                }
            }
    

    您正在事件调度线程中执行长时间运行的功能。 GUI 在事件调度线程中更新,因此在您退出侦听器之前,不会看到任何 GUI 更改。试试这个:

            @Override public void keyPressed(KeyEvent e){
                if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                    Thread t = new Thread(new Runnable() {
                      _gui.somethingChaged1();// CHANGE 1 GUI
                      public void run() {
                        _logic.thinking();// LOGIC PROCESSING (3sec)
                        //Changed based on comment from daiscog
                        SwingUtilities.invokeLater(new Runnable() {
                          public void run() {
                            _gui.somethingChaged2();// CHANGE 2 GUI
                          }
                        });
                      }
                    }, "Logic Code");
                    t.start();
    
                    e.consume();// Stopping adding an Enter after message
                }
            }
    

    给你的线程命名也是一个很好的做法,这样如果你必须调试你可以很容易地找到你创建的线程。

    【讨论】:

    • @AdrianSimionescu,您对问题的逻辑是正确的。也就是说,在任务完成执行之前,GUI 无法自行重绘。但是,解决方案是错误的。 InvokeLater() 方法在 EDT 上执行代码,因此您没有更改任何内容。解决方案是在单独的线程上执行长时间运行的任务,这样您就不会阻塞 EDT。一种方法是使用SwingWorker。阅读Concurrency 上的 Swing 教程以获取更多信息和示例。
    • 我移动了 _gui.somethingChaged1();在 invokeLater 之前,它正在工作。这是一个好习惯吗?
    • @camickr 是对的。自从我做 Swing 以来已经有一段时间了。我将更改代码以反映正常线程。
    • @markbernard 在事件分派线程之外更新 GUI(_gui.somethingChaged1()_gui.somethingChaged2())是危险的。正确的解决方案是从 EDT 调用 _gui.somethingChaged1(),然后启动一个调用 _logic.thinking(); 的新线程,然后将另一个 Runnable 传递给 invokeLater() 以调用 _gui.somethingChaged2()
    • @daiscog 不正确。 Swing 不是线程安全的,这不是一回事。您可以对 Swing 应用程序中的底层对象进行更改,但在调用在事件调度线程上运行的 repaint 之前,这些更改将不可见。在这种情况下,他不希望在逻辑完成之前第二次更新 GUI。他可以在新线程之外进行第一次更新。
    猜你喜欢
    • 2019-06-19
    • 1970-01-01
    • 1970-01-01
    • 2015-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-08
    • 1970-01-01
    相关资源
    最近更新 更多