JTextArea update VERY slow
I have a dialog that a JTabbedPane with several tabs on it. One of the tabs contains a JTextArea that I have redirected System.out to. The JTextArea is attached to a JScrollPane and then to the JTabbedPane. The class I created to send System.out to the JTextArea does a JTextArea.append() to display the information in the JTextArea. I am NOT trying to scroll the JScrollPane to keep the last message displayed or anything like that.
The problem is, when I write a large amount of data (30-50 lines, each line <60 characters) to the JTextArea (with System.out.println()) my application becomes unresponsive and it takes a very long time to display. This is only an issue when I have selected the tab in the JTabbedPane that contains my JTextArea. If I select another tab, writing a large amount of data to System.out has no noticeable affect on my application and it displays very quickly.
Is there a better way to update the JTextArea than to use JTextArea.append()? The append() method seems to invalidate the entire JTextArea for each line or character that is displayed (even when the text is not actually displayed on the screen) and slows things down a lot.
I just added some Duke Dollars to this for anyone who wants to take a shot. It seems like I am not the only one who has seen this issue before as I have searched the forums and found similar issues with no resolution.
Yes, it's almost certainly due to the revalidation occurring for each line of text.
If you turn off auto flushing on your PrintStream you could then use a Thread that regularly flushes the stream to the text area, hopefully improving performance.
public class PrintStreamFlusher implements Runnable
{
private PrintStream printStream;
private long updateInterval;
public PrintStreamFlusher(PrintStream printStream, long updateInterval)
{
this.printStream = printStream;
this.updateInterval = updateInterval;
}
public void run()
{
while(Thread.currentThread() == thread)
{
try
{
Thread.sleep(updateInterval);
flush();
}
catch(InterruptedException e)
{
}
}
}
public void flush()
{
printStream.flush();
}
}
Don't know how your PrintStream to the text area is implemented so you'll have to disable automatic flushing on your own (unless you post the code for it!).
Hope this helps.
That seems to make things work better! The only thing that I had to change was that I had to wrap my stream (which simply extended OutputStream) in a BufferedOutputStream. Just telling the PrintStream not to automatically flush was not enough.
Glad it helped you.I'd be interested to see your code - I've written something similar once but wasn't particularly happy with it.
I can't really say that it is that exciting but, here it is
import java.awt.*;
import java.io.*;
import javax.swing.*;
import javax.swing.text.*;
public class GUIStream extends OutputStream {
private JTextArea_area= new JTextArea();
private JScrollPane_pane= new JScrollPane( _area );
public GUIStream() {
_pane.setPreferredSize( new Dimension( 450, 125 ) );
_area.setEditable( false );
}
public JComponent getOutput() {
return _pane;
}
public void write( int b ) {
String out;
try {
out = Integer.toString( b );
}
catch( Exception e ) {
return;
}
_area.append( out );
_area.invalidate();
}
public void write( byte[] buf ) throws IOException {
write( buf, 0, buf.length );
}
public void write( byte[] buf, int off, int len ) {
_area.append( new String( buf, off, len ) );
_area.invalidate();
}
public void clear() {
_area.setText( "" );
}
}
To redirect System.out, i simply did this:
System.setOut( new PrintStream( new BufferedOutputStream( new GUIStream() ), false );
To add the Component to a dialog, or whatever, use GUIStream.getOutput().
BTW, remove _area.invalidate() from the two write() methods. It is not needed and slows things down a little bit.