如何刷新Java的JTextArea?

DJa*_*mes 2 java concurrency user-interface swing jtextarea

我正在用一个按钮编写一个GUI.当用户单击该按钮时,我希望立即在JTextArea中显示"Beginning work ..."消息,并显示"已完成".工作完成时显示的消息.GUI包含表单的一些代码

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

    <more lines of code>

    myJTextArea.append("Finished.\n");
}
Run Code Online (Sandbox Code Playgroud)

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

谢谢

Hov*_*els 5

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

这与"刷新"JTextArea无关,而是与确保您的代码遵循Swing线程规则有关.输出到JTextArea的长时间运行的代码应该在后台线程中调用,例如使用SwingWorker.然后它将间歇性地将其结果输出到JTextArea,但确保在Swing事件线程上小心地这样做.如果您使用SwingWorker,可以使用发布/处理方法对完成.

看看:教程: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);
}
Run Code Online (Sandbox Code Playgroud)

代码改编自我的代码,回答了以前的类似问题.


注意,如果您要使用标准的Runnable和没有SwingWorker的后台线程,正如Lars所建议的那样,您首先要实现Runnable而不是扩展Thread,并且您必须注意大多数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();
}
Run Code Online (Sandbox Code Playgroud)

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