使用线程实时更新JTextArea

时间:2014-11-14 14:43:16

标签: java multithreading swing jtextarea

我正在尝试随机生成一个字符串并不断更新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();

            }
        });

    }

}

3 个答案:

答案 0 :(得分:3)

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

就此而言,为什么你甚至需要在while (true)方法中使用runTest()循环?它始终由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中。对于任何语法错误,我们深表歉意。

答案 1 :(得分:1)

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

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

从我现在的代码中可以看出,你通过在一个不变的循环中冻结它来锁定你的应用程序。

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

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

答案 2 :(得分:0)

试试这个。

 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就不会打电话......