问题
I have code that redirects system output to a jtext area, but that jtextarea doesnt update until the code is finished running. How do I modify the code to make the jtextarea update in real time during runtime?
private void updateTextArea(final String text) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
consoleTextAreaInner.append(text);
}
});
}
private void redirectSystemStreams() {
OutputStream out = new OutputStream() {
@Override
public void write(int b) throws IOException {
updateTextArea(String.valueOf((char) b));
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
updateTextArea(new String(b, off, len));
}
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
};
System.setOut(new PrintStream(out, true));
System.setErr(new PrintStream(out, true));
}
The rest of the code is mainly just an actionlistener for a button:
private void updateButtonActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
String shopRoot = this.shopRootDirTxtField.getText();
String updZipPath = this.updateZipTextField.getText();
this.mainUpdater = new ShopUpdater(new File(shopRoot), updZipPath);
this.mainUpdater.update();
}
That update() method begins the process of copying+pasting files on the file system and during that process uses system.out.println to provide an up-to-date status on where the program is currently at in reference to how many more files it has to copy.
回答1:
It's hard to tell with 100% assuredly what is going wrong, as there's a lot of critical involved code that we're not seeing, but there's a good chance that you're running into a Swing threading issue. You need to read the stream in a background thread and then call your update to text area method on the Swing event thread, the EDT. Otherwise you'll tie up the EDT while waiting for the stream to read, completely freezing your GUI. A SwingWorker would work well. Consider publishing to the JTextArea each time a new line is encountered.
For example:
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class MyRedirectStream {
private JTextArea textArea;
public MyRedirectStream(JTextArea gui) {
this.textArea = gui;
}
public void redirect() {
System.setOut(new PrintStream(new MyOutStream(), true));
System.setErr(new PrintStream(new MyOutStream(), true));
}
private class MyOutStream extends OutputStream {
private static final int MAX_LENGTH = 1600;
private StringBuilder sb = new StringBuilder(MAX_LENGTH + 100);
@Override
public void write(int b) throws IOException {
sb.append((char)b);
if (sb.length() > MAX_LENGTH) {
sendToTextArea(sb.toString());
sb = new StringBuilder();
}
}
@Override
public void flush() throws IOException {
sendToTextArea(sb.toString());
sb = new StringBuilder();
super.flush();
}
private void sendToTextArea(final String text) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
textArea.append(text);
}
});
}
}
}
and:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class StreamToTextArea2 {
public static void main(String[] args) {
final TextPanel gui = new TextPanel();
final MyRedirectStream myRedirectStream = new MyRedirectStream(
gui.getTextarea());
myRedirectStream.redirect();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final JFrame frame = new JFrame("Redirect");
JButton showTextBtn = new JButton(new AbstractAction("Show Text") {
@Override
public void actionPerformed(ActionEvent arg0) {
JFileChooser fileChooser = new JFileChooser();
int result = fileChooser.showOpenDialog(frame);
if (result == JFileChooser.APPROVE_OPTION) {
gui.clearText();
final File file = fileChooser.getSelectedFile();
new Thread(new Runnable() {
public void run() {
try {
Scanner scan = new Scanner(file);
while (scan.hasNextLine()) {
System.out.println(scan.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}).start();
}
}
});
JButton clearTextBtn = new JButton(
new AbstractAction("Clear Text") {
@Override
public void actionPerformed(ActionEvent e) {
gui.clearText();
}
});
JPanel btnPanel = new JPanel();
btnPanel.add(clearTextBtn);
btnPanel.add(showTextBtn);
frame.getContentPane().add(gui);
frame.getContentPane().add(btnPanel, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
import java.awt.BorderLayout;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
class TextPanel extends JPanel {
private JTextArea textarea = new JTextArea(20, 40);
public TextPanel() {
setLayout(new BorderLayout());
JScrollPane scrollPane = new JScrollPane(textarea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
add(scrollPane);
}
public JTextArea getTextarea() {
return textarea;
}
public void clearText() {
textarea.setText("");
}
}
回答2:
You did write invokeLater, and it does invoke it later. How much late is later is indeterminate unless you perform something active to make it determinate. You could, for example, insert Thread.sleep calls between files.
来源:https://stackoverflow.com/questions/19716664/java-redirected-system-output-to-jtext-area-doesnt-update-until-calculation-is