如何刷新到 Java 的 JTextArea?

How do I flush to Java's JTextArea?

我正在编写带有按钮的 GUI。当用户单击按钮时,我希望 "Beginning work..." 消息立即出现在 JTextArea 中,并在工作完成后出现 "Finished." 消息。 GUI 包含一些形式的代码

private void buttonActionPerformed(java.awt.event.ActionEvent evt) {
    myJTextArea.append("Beginning work...\n");

    <more lines of code>

    myJTextArea.append("Finished.\n");
}

不幸的是,这两条消息直到最后才出现。有没有办法将消息刷新到 JTextArea?在另一个论坛上,我看到有人提到 运行 JTextArea 输出的单独线程。基于此的一些解决方案是否可能?

谢谢

Unfortunately, neither message appears until the end. Is there a way to flush messages to JTextArea? On another forum, I saw mention running a separate thread for the JTextArea output. Would some solution based on that be possible?

这与 "flushing" JTextArea 无关,与确保您的代码遵循 Swing 线程规则有关。输出到 JTextArea 的长 运行 代码应该在后台线程中调用,例如使用 SwingWorker。然后它会间歇性地将其结果输出到 JTextArea,但一定要在 Swing 事件线程 上小心地输出结果 。如果您使用 SwingWorker,这可以使用 publish/process 方法对来完成。

看看:Tutorial: Concurrency in Swing

例如:

import java.util.List;
import javax.swing.*;

public class SwingThreadingEg extends JPanel implements MyAppendable {
    private JTextArea area = new JTextArea(30, 50);

    public SwingThreadingEg() {
        JScrollPane scrollPane = new JScrollPane(area);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        add(scrollPane);
    }

    @Override
    public void append(String text) {
        area.append(text);
    }

    private static void createAndShowGui() {
        SwingThreadingEg mainPanel = new SwingThreadingEg();
        MyWorker myWorker = new MyWorker(mainPanel);
        // add a Prop Change listener here to listen for
        // DONE state then call get() on myWorker
        myWorker.execute();

        JFrame frame = new JFrame("SwingThreadingEg");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGui();
            }
        });
    }
}

class MyWorker extends SwingWorker<Void, String> {
    private static final long SLEEP_TIME = 500;
    private MyAppendable myAppendable;

    public MyWorker(MyAppendable myAppendable) {
        this.myAppendable = myAppendable;
    }

    @Override
    protected Void doInBackground() throws Exception {
        publish("Beginning Work");

        // simulate some long-running task:
        for (int i = 0; i < 20; i++) {
            publish("From SwingWorker: " + i);
            Thread.sleep(SLEEP_TIME);
        }
        publish("Finished!");
        return null;
    }

    @Override
    protected void process(List<String> chunks) {
        for (String text : chunks) {
            myAppendable.append(text + "\n");
        }
    }
}

interface MyAppendable {
    public void append(String text);
}

代码改编自我对 .

的回答的代码

请注意,如果您要使用标准的 Runnable 和不带 SwingWorker 的后台线程,正如 Lars 所建议的那样,您首先要实现一个 Runnable 而不是扩展线程,并且您 必须注意大多数Swing调用都在事件线程上排队,因此类似于:

@Override
public void actionPerformed(ActionEvent e) {
    textarea.append("Start...");
    new Thread(new Runnable(){
        @Override
        public void run() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    textarea.append("End");
                }
            });
        }
    }).start();
}

这看起来有点复杂,所有嵌套的匿名内部 类,事实上,这是 SwingWorker 在这种情况下可能工作得更好的主要原因之一,因为使用的代码更简单,减少产生看不见的错误的机会。

另一个没有工人的例子,只使用线程:

package swingexperiments;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class SwingExperiment {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGui();
            }
        });
    }

    private static void createAndShowGui() {
        JFrame jFrame = new JFrame("Test");
        jFrame.setLayout(new BorderLayout());
        JTextArea textarea = new JTextArea();
        jFrame.add(textarea, BorderLayout.CENTER);
        jFrame.add(new JButton(new AbstractAction("Test") {
            @Override
            public void actionPerformed(ActionEvent e) {
                textarea.append("Start..."); // here we are on the event dispatcher thread
                new Thread() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                textarea.append("End");
                            }
                        });
                    }
                }.start();
            }
        }), BorderLayout.SOUTH);
        jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        jFrame.pack();
        jFrame.setSize(1000, 800);
        jFrame.setVisible(true);
    }

}