22

I am trying to implement a JScrollPane with a JTextArea. The JTextArea is being appended to, and I want the JScrollPane to keep scrolling down as more text is added. How can this be achieved?

Krigath
  • 321
  • 1
  • 2
  • 6

8 Answers8

42

For (what I think is) a simpler answer check out: Text Area Scrolling.

Prior to JDK5, you would have to manually change the caret's position after each append. You can now give this behaviour as a default like this :

 JTextArea textArea = new JTextArea();
 DefaultCaret caret = (DefaultCaret)textArea.getCaret();
 caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);

The advantage of this is that you don't need to use this snippet more than once in your code!

Docteur
  • 1,235
  • 18
  • 34
camickr
  • 321,443
  • 19
  • 166
  • 288
  • +1 The `setUpdatePolicy()` API explains _so_ much. I've updated my answer to the cited question accordingly. – trashgod Mar 20 '10 at 17:03
  • Yes, the API keeps changing its hard to keep up with changes all the time. – camickr Mar 20 '10 at 18:54
  • This helps, but if the users clicks anywhere other than after the end of the last line the auto scrolling stops. This is not intuitive. It would be nice if the usual behaviour worked, just by scrolling to the bottom the auto-scroll is reactivated. – jcalfee314 Nov 19 '13 at 19:12
  • 2
    @jcalfee314, Check out [Smart Scrolling](http://tips4java.wordpress.com/2013/03/03/smart-scrolling/) for another potential solution. – camickr Nov 19 '13 at 19:22
  • @jcalfee314 To fix this, I added a button which, when clicked, enabled auto scrolling again. The code for the button only needs this: textArea.setCaretPosition(textArea.getDocument().getLength()); – Rok T. Sep 22 '17 at 11:08
  • @jcalfee314 Here is an [answer](https://stackoverflow.com/a/70479122/7269325) for that issue . – Krisztian Nagy Zsolt Dec 25 '21 at 09:55
9

I found the answer here: JScrollPane and JList auto scroll

scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {  
        public void adjustmentValueChanged(AdjustmentEvent e) {  
            e.getAdjustable().setValue(e.getAdjustable().getMaximum());  
        }
    });
Community
  • 1
  • 1
Krigath
  • 321
  • 1
  • 2
  • 6
  • 7
    Your "solution" is actually the problem of the linked-to question: if you manually try to move the vertical scroll handle, this code will scroll to the bottom again! – jackrabbit Mar 20 '10 at 17:09
  • @jackrabbit [Here](https://stackoverflow.com/a/70479122/7269325) is a code to make it stop scrolling if the user manually moves the vertical scrollbar. It continues to scroll if the user scrolls to the bottom. – Krisztian Nagy Zsolt Dec 25 '21 at 10:31
7

If you are constantly writing data to it you could use:

textArea.setCaretPosition(textArea.getDocument().getLength());

just after you add the new data.

This would automatically scroll all the way down the JScorllPane.

Kijewski
  • 25,517
  • 12
  • 101
  • 143
Harold Martin
  • 71
  • 1
  • 2
5

Here is the solution.

JTextArea textArea = new JTextArea();
DefaultCaret caret = (DefaultCaret)textArea.getCaret();
caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);` 
Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
2

The accepted solution works good, but only when the text area is editable, i.e. without jTextArea.setEditable(false) . The solution suggested by Krigath is more general, but has the problem as asked here JScrollPane and JList auto scroll. Using answers from that question you can get general solution, e.g.:

        JScrollPane scrollPane = new JScrollPane(jTextArea);

    verticalScrollBarMaximumValue = scrollPane.getVerticalScrollBar().getMaximum();
    scrollPane.getVerticalScrollBar().addAdjustmentListener(
            e -> {
                if ((verticalScrollBarMaximumValue - e.getAdjustable().getMaximum()) == 0)
                    return;
                e.getAdjustable().setValue(e.getAdjustable().getMaximum());
                verticalScrollBarMaximumValue = scrollPane.getVerticalScrollBar().getMaximum();
            });

The Pane then is scrolled down only when vertical scroll bar is expanding, in response to appended lines of text.

I admit that that a method to filter events without extra variables could be found, and would appreciate if somebody post it.

user9999
  • 113
  • 2
  • 10
1

A work around is possible: you can declare that listener as a class then instantiate it on the event where it is needed. After which you can remove the class after forcing a repaint of the screen. Works like a charm.

Cosmin
  • 21,216
  • 5
  • 45
  • 60
Primark
  • 36
  • 1
0

I was look at the answers and found that @user9999 answer is a good solution for those who want the scrollbar continuously scroll. I edited the code, got rid of the variable. It make the scrolling stop - when the user is scrolling manually. If the user scrolls to the end of the textarea or scrollarea, the auto scrolling continues.

(also as @user9999 suggested i removed the variable and added the jScrollPane1.getHeight() as the measure value to stop scrolling if the current scrollbar value is lower than max)

Here is the workaround:

jScrollPane1.getVerticalScrollBar().addAdjustmentListener(
e -> {
    if ((e.getAdjustable().getValue() - e.getAdjustable().getMaximum()) > -jScrollPane1.getHeight() - 20){
        e.getAdjustable().setValue(e.getAdjustable().getMaximum());
    }   
});

Edit:

Added -20 to the -jScrollPane1.getHeight() - 20 as it is sometimes doesnt scroll without it, i guess the -20 can be changed depends on the font size of the TextArea.

0

Be careful if you're about to use auto scroll within a multithreaded program. Like for example if somewhere is a method like

public void addNewLine(String s){
        textPane.setText(textPane.getText()+"\n"+s);
        if(autoscrollCheckBox.isSelected()){
            this.revalidate();
            JScrollBar vertical = scrollPane.getVerticalScrollBar();
            vertical.setValue(vertical.getMaximum());
        }
}

You will get the following exception, if the addNewLine (or the vertical.setValue(...)) method is called from another thread. (Especially, if you're try to resize the window or try to scroll while, autoscroll is enabled)

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.text.BoxView.calculateMajorAxisRequirements(BoxView.java:871)
at javax.swing.text.BoxView.checkRequests(BoxView.java:930)
at javax.swing.text.BoxView.getMinimumSpan(BoxView.java:568)
...

The reason for this is, the multithreaded call of the method is messing up with Swings' eventhandling, so you'll get random results or like above an error (read morehere).

The correct way is to call SwingUtilities.invokeLater(...):

public void addNewLine(String s){
       SwingUtilities.invokeLater( () -> {
            textPaneOutput.setText(textPaneOutput.getText()+"\n"+s);
            if(autoscrollCheckBox.isSelected()){
                this.revalidate();
                JScrollBar vertical = scrollPane.getVerticalScrollBar();
                vertical.setValue(vertical.getMaximum());
            }
        });
 }

This way you'll able to auto scroll threadsafe!

BeRational
  • 46
  • 5