Autoscrolling JTextArea + JScrollPane
November 6, 2008 7:32 AM   Subscribe

Java & JTextArea + JScrollpane: How do I autoscroll while text is being appended to the TextArea?

In a project I am working on I have a JTextArea which is used to log operations as they occur. It reports the progress of various operations. The problem is that there are many such operations, so the TextArea fills up and text continues being output to the frame. I would like for that frame to autoscroll while the text is being written to it.

Right now the frame will only scroll to the bottom once all the text has been written to it. Before the text wouldn't even appear before all the operations were done; I fixed that by calling RepaintAll for the jScrollPane. Now I can see the text and I can see it flicker as it updates but the scrollpane stays at the top until the end of all operations. How can I modify this behavior?

I've abstracted this to a small test app. The code is as follows:
package mywork.scrolltest.client;import java.awt.Dimension;import java.awt.Rectangle;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JScrollPane;import javax.swing.JTextArea;public class FrmMain extends JFrame {    private JTextArea jtaLog = new JTextArea();    private JScrollPane jspLog = new JScrollPane(jtaLog);    private JButton jbGo = new JButton();    public FrmMain() {        try {            jbInit();        } catch (Exception e) {            e.printStackTrace();        }    }    public static void main (String[] args) {        FrmMain frmMain = new FrmMain();    }    private void jbInit() throws Exception {        this.getContentPane().setLayout( null );        this.setSize(new Dimension(400, 434));        this.addWindowListener(new WindowAdapter() {            public void windowClosing(WindowEvent e) {                this_windowClosing(e);            }        });         jspLog.setBounds(new Rectangle(10, 10, 375, 340));        jbGo.setText("Go");        jbGo.setBounds(new Rectangle(150, 365, 73, 22));        jbGo.setActionCommand("jbGo");        jbGo.addActionListener(new ActionListener() {            public void actionPerformed(ActionEvent e) {                jbGo_actionPerformed(e);            }        });        jtaLog.setAutoscrolls(true);        jspLog.setAutoscrolls(true);                this.getContentPane().add(jspLog, null);        this.getContentPane().add(jbGo, null);                this.setVisible(true);    }    private void jbGo_actionPerformed(ActionEvent e) {        if ("jbGo".equals(e.getActionCommand())) {            for (Integer i=0; i<5000; i++) {                jtaLog.append("Test string " + i + "\n");                jspLog.paintAll(jspLog.getGraphics());            }        }    }    private void this_windowClosing(WindowEvent e) {        System.exit(0);    }}
posted by splice to Computers & Internet (14 answers total) 1 user marked this as a favorite
 
setCaretPosition(textArea.getDocument().getLength())
posted by zeoslap at 7:37 AM on November 6, 2008


zeoslap has it.
posted by Echidna882003 at 7:41 AM on November 6, 2008


Response by poster: Thanks. I assume by this you mean:
jtaLog.setCaretPosition(jtaLog.getDocument().getLength());
I have tried inserting this before and after the repaint. It does not work. I also recall debugging and checking the caret position. It does move but for some reason the scrollbar stays at the very top until all the text has been written.
posted by splice at 7:42 AM on November 6, 2008


Try getLength() -1.
posted by zeoslap at 7:49 AM on November 6, 2008


Response by poster: No, that doesn't work either. Again, I debugged and checked the caret position. It does change. The problem is that even with repaints the scrollbar stays at the top until the end of all output.

Please try the code out. You can see for yourself what happens and that your solution does not solve this issue.
posted by splice at 7:52 AM on November 6, 2008


jspLog.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
jtaLog.select(jtaLog.getCaretPosition() * 11, 0);
}
});

add this to init where 11 is the fontsize
posted by zeoslap at 8:05 AM on November 6, 2008


Response by poster: Yes, I recall trying that too. I tried it now again. It does not work.

Really, I appreciate the attempts to help. But the code is right there, I would appreciate it if you could test your solution. If it does not work it's not what I am looking for. I can do google searches as good as anyone and I have tried the solutions you've provided and probably others I cannot recall right now. The reason I am asking here is because nothing I have found works.

I believe I may be looking for something similar to VB's DoEvents. The scrollbar simply seems not to process any events until the loop is done, even with delays inserted in the loop.
posted by splice at 8:13 AM on November 6, 2008


I hear ya. I'll keep schtum till it works (I have your code running), odd, puzzling and frustrating :)
posted by zeoslap at 8:15 AM on November 6, 2008


Response by poster: I think I may be on the right track... I found people looking for similar answers, and threading seems to be suggested. There is something called SwingWorker that seems to be squarely aimed at this... I am reading more and will see what modifications I can make.
posted by splice at 8:18 AM on November 6, 2008


Response by poster: And FYI: SwingWorker
posted by splice at 8:19 AM on November 6, 2008


Best answer: private void jbGo_actionPerformed(ActionEvent e) {
if ("jbGo".equals(e.getActionCommand())) {
for (Integer i = 0; i <> jtaLog.setCaretPosition(jtaLog.getText().length());
JScrollBar vbar = jspLog.getVerticalScrollBar();
boolean autoScroll = ((vbar.getValue() + vbar.getVisibleAmount()) == vbar.getMaximum());
jtaLog.append("Test string " + i + "\n");
jspLog.getViewport().setViewPosition(new Point(0, 11*i));

try {
Thread.sleep(100);
} catch (InterruptedException e1) {
}
jspLog.paintAll(jspLog.getGraphics());
}
}
}
posted by zeoslap at 8:27 AM on November 6, 2008


Setting the viewport works, my calcs are a bit off (i * 11) but the principal works.
posted by zeoslap at 8:30 AM on November 6, 2008


i * 20 does the trick, but you'll want to grab the correct font size
posted by zeoslap at 8:36 AM on November 6, 2008


Response by poster: The code came through a bit mangled and the implementation is a bit buggy but this seems to work. Just need to modify it a bit for my purposes. Thanks!
posted by splice at 8:37 AM on November 6, 2008


« Older Bad Mitsubishi TV   |   Office Wizard Newer »
This thread is closed to new comments.