【问题标题】:Using threading to update JTextArea in realtime使用线程实时更新 JTextArea
【发布时间】:2014-11-14 14:43:15
【问题描述】:

我正在尝试随机生成一个字符串并不断更新一个 JTextArea。我知道程序在无限循环中挂断在 runTest() 方法中。我试图循环并显示这个结果,直到用户点击停止按钮。有什么建议吗?谢谢

    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JTextArea;
    import javax.swing.SwingUtilities;
    import java.awt.BorderLayout;
    import java.awt.Panel;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.Random;

public class MyApplication extends JFrame {
    private JTextArea textArea;
    private JButton b;
    private String toChange = "";

    // Called from non-UI thread
    private void runQueries() {
        while (true) {
            runTest();
            updateProgress();
        }
    }

    public void runTest() {

        while (true) {

            if (toChange.length() > 10) {
                toChange = "";
            }
            Random rand = new Random();
            toChange += rand.nextInt(10);
        }
    }

    private void updateProgress() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                textArea.append(toChange);
            }
        });
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                MyApplication app = new MyApplication();
                app.setVisible(true);
            }
        });
    }

    private MyApplication() {

        this.setLayout(null);
        this.setResizable(false);
        this.setLocation(100, 100);
        this.setSize(900, 600);

        final Panel controlPanel = new Panel();
        controlPanel.setLayout(new BorderLayout());
        controlPanel.setSize(600, 200);
        controlPanel.setLocation(50, 50);
        setDefaultCloseOperation(this.EXIT_ON_CLOSE);

        textArea = new JTextArea("test");
        textArea.setSize(100, 100);
        textArea.setLocation(200, 200);
        this.add(textArea);

        JButton b = new JButton("Run query");
        b.setSize(100, 75);
        b.setLocation(100, 50);
        this.add(b);

        b.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                Thread queryThread = new Thread() {
                    public void run() {
                        runQueries();

                    }
                };
                queryThread.start();

            }
        });

    }

}

【问题讨论】:

  • 我认为您希望在新线程上执行重复任务。给我一分钟来获取代码。
  • Thread queryThread 设为class 变量并在“停止”按钮的actionPerformed() 方法中执行queryThread.interrupt()。请参阅Java Thread Primitive Deprecation 了解更多信息。
  • Java GUI 必须在不同的操作系统、屏幕尺寸、屏幕分辨率等上工作。因此,它们不利于像素完美布局。而是使用布局管理器,或 combinations of them 以及 white space 的布局填充和边框。
  • 参见here 显示的示例。希望如果您阅读了整个主题,您会学到很多关于 Swing 并发的知识。同样可以启动和停止的 Swing Timer 也可以用作 SwingWorker 的替代品。

标签: java multithreading swing jtextarea


【解决方案1】:
int initialDelay = 1000; // start after 1 seconds
    int period = 1000;        // repeat every 1 seconds
    Timer timer = new Timer();
    TimerTask task = new TimerTask() {
      public void run() {
         if (toChange.length() > 10) {
            toChange = "";
        }
        Random rand = new Random();
        toChange += rand.nextInt(10);
    };
    timer.scheduleAtFixedRate(task, initialDelay, period);

你会想要像上面我刚刚做的那样的东西。否则,您只是在冻结线程,因此永远不会调用 updateProgress();

关于这个问题,为什么在 runTest() 方法中还需要一个 while (true) 循环?无论如何,runQueries() 中的 while true 循环一直在运行和更新它,这意味着 while (true) 循环似乎没有任何意义。

澄清: 我是说你应该把你的 runTest() 和 updateProgress() 放在一个延迟的任务中,而不是一个真正的循环 - 否则,你只是通过把它们放在同一个线程中来阻止另一个任务。

但是,更改此设置也应该可以解决问题:

b.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
           int initialDelay = 100; // start after 0.1 seconds
           int period = 100;        // repeat every 0.1 seconds
        {
            Timer timer = new Timer();
            TimerTask task = new TimerTask() {
            public void run() {
                runTest();
            };
        timer.scheduleAtFixedRate(task, initialDelay, period);
        }
        {
            Timer timer = new Timer();
            TimerTask task = new TimerTask() {
            public void run() {
                updateProgress();
            };
        timer.scheduleAtFixedRate(task, initialDelay, period);
        }
    }
    });

}

还有 runTest()..

public void runTest() {
        if (toChange.length() > 10) {
            toChange = "";
        }
        Random rand = new Random();
        toChange += rand.nextInt(10);
}

和 updateProgress()..

private void updateProgress() {
    textArea.append(toChange);
}

请注意,我是在 SO 编辑器中输入代码,而不是在 IDE 中。很抱歉有任何语法错误。

【讨论】:

  • 我很难理解这一点,你能用我给定的例子详细说明吗? Timer 应该在哪里创建?谢谢
  • @JavaNoob 我添加了一些说明以及我之前没有意识到的解决方案。
  • 啊,解决方案显示一次然后冻结程序。
  • @JavaNoob 立即尝试。它应该不断地在不同的线程上运行 updateProgress() 和 runTest()。
  • 我看到你在这里想要实现的目标,但我遇到了编译错误......这些是正确的导入吗?导入 java.util.TimerTask;导入 javax.swing.Timer;
【解决方案2】:

我不能强调的最重要的事情之一是 Java 中的所有 GUI 元素都应该在 EventDispatchThread 上运行,否则会导致不可预测的错误行为。

在此处阅读:https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html

从您当前的代码可以看出,您正在通过将应用程序冻结在一个恒定循环中来活锁您的应用程序。

您要做的是创建一个单独的线程,该线程在创建时会不断更新 EDT 中的 TextArea,但在您的主应用程序中保持引用该线程。 一旦用户按下按钮(这将在 EDT 上),它应该告诉您的主线程杀死您的单独线程,从而导致更新停止。

如果您遇到困难,请随时询问更详细的步骤。

【讨论】:

    【解决方案3】:

    试试这个。

     private void runQueries() {
        while (!stop) {
            runTest();
            updateProgress();
        }
    }
    

    make stop 在你想要的时候是真的,例如点击停止按钮。那么

    public void runTest() {
    
        while (true) {
    
            if (toChange.length() > 10) {
                toChange = "";
                break;  
            }
            Random rand = new Random();
            toChange += rand.nextInt(10);
        }
    }
    

    如果你不打破循环,你的updateProgress 将不会调用..

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-21
      • 1970-01-01
      • 2015-08-03
      • 2014-12-11
      • 1970-01-01
      • 2019-01-26
      • 2018-11-26
      相关资源
      最近更新 更多