54

I need to scroll a JScrollPane to the bottom. The JScrollPane contains a JPanel, which contains a number of JLabels.

To scroll to the top, I just do:

scrollPane.getViewport().setViewPosition(new Point(0,0));

But how do I scroll exactly to the very bottom? (Too far and it jitters)

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
Matt
  • 11,157
  • 26
  • 81
  • 110

12 Answers12

120
JScrollBar vertical = scrollPane.getVerticalScrollBar();
vertical.setValue( vertical.getMaximum() );
camickr
  • 321,443
  • 19
  • 166
  • 288
  • @Jeff: Matt's subsequent [answer](http://stackoverflow.com/a/9998095/230513) suggests the use of `setUpdatePolicy()`, also [examined](http://tips4java.wordpress.com/2008/10/22/text-area-scrolling/) by camickr – trashgod Jan 15 '13 at 10:07
  • 2
    I happen to have it scrolled just above the last added panel. That is, in my chat UI, you always have the last message hidden. – Tomáš Zato Apr 30 '14 at 09:47
  • 10
    Note that you have to call on `validate()` on the component with the scroll bar, before you set the scroll bar to its maximum value. This is to ensure that you get the latest up-to-date maximum value, if you added/removed an element from the component. SSCCE version here on Pastebin: http://pastebin.com/v1xi35ZK – tom_mai78101 Jun 23 '14 at 04:18
  • @tom_mai78101, `if you added/removed an element from the component` - then you should use revalidate() (which is used for Swing), not validate() (which was used with AWT). – camickr Jun 23 '14 at 20:26
  • @camickr From http://stackoverflow.com/a/9511880/1016891, there seems to be no difference between Swing's and AWT's `validation()` methods. – tom_mai78101 Jun 24 '14 at 06:25
  • @tom_mai78101, yes, they are very similar and the majority of time either method will work, however, this method was specifically added for Swing because Swing is different than AWT. Read the API. – camickr Jun 24 '14 at 14:08
  • The last line was not displayed as long I wasn't on the AWT Dispatching Thread. The solution therefore was `this.historyList.addPropertyChangeListener("model", (e) -> { SwingUtilities.invokeLater( () -> { scrollPane.getVerticalScrollBar().setValue(Integer.MAX_VALUE); }); } );` – Olivier Faucheux Dec 12 '19 at 21:59
  • I have an odd behavior in my code. The Answer only works for the first 14 entrys in my JScrollPane then it stops to scroll further down. It just sticks at the 14 while I add new entry's to the JScrollPane. – Georodin Oct 13 '20 at 11:45
40

After many hours of attempting to find an answer other than one using the scrollRectToVisible() method, I've succeeded. I've found that if you use the following code after you output text to the text area in the scrollpane, it will automatically focus on the bottom of the text area.

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

So, at least for me, my print method looks like this

public void printMessage(String message)
{
    textArea.append(message + endL);
    textArea.setCaretPosition(textArea.getDocument().getLength());
}
Decesus
  • 507
  • 4
  • 10
  • In the particular case of a `JTextComponent`, also consider `DefaultCaret#setUpdatePolicy()`, illustrated [here](http://stackoverflow.com/a/3245805/230513). – trashgod Jan 15 '13 at 09:50
  • 1
    I found this answer especially helpful because in my case I'm using a custom sub-class that inherits from JTextPane where I'm already using my own custom print routine, so it was super convenient to just add that line of code to the end of the method definition! – Dyndrilliac Apr 09 '14 at 06:47
30
scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {  
    public void adjustmentValueChanged(AdjustmentEvent e) {  
        e.getAdjustable().setValue(e.getAdjustable().getMaximum());  
    }
});
Peter Saitz
  • 319
  • 3
  • 2
  • 4
    +1 Even though it had a score of zero, this worked better for me than camickr's answer. The latter solution wouldn't always scroll entirely to the bottom immediately after I insertString to the text pane. This solution always scrolls to the very bottom. – jk. Nov 04 '13 at 06:56
  • 8
    One issue with this solution is that it no longer allows you to scroll up. You need to make a custom AdjustmentListener that allows 'e.getAdjustable.setValue(...)' to be turned off when the user is allowed to scroll up. – jk. Nov 04 '13 at 07:01
  • 1
    My solution was to set a boolean flag for when I added content dynamically and check that flag before scrolling and then always set it to false afterward. That way user scrolling would never be affected. – Bjorn Mar 12 '14 at 22:47
  • 1
    Fixed @jk's problem... Try `if(e.getAdjustable().getMaximum() != prevMax) {/*set the value*/}` – Peter Gordon Apr 24 '15 at 17:58
  • 2
    Those who have the jk. problem, check the @Matthias Braun solution below. It works perfectly. – Emadpres Jan 01 '17 at 11:39
18

I adapted the code of Peter Saitz. This version leaves the scrollbar working after it has finished scrolling down.

private void scrollToBottom(JScrollPane scrollPane) {
    JScrollBar verticalBar = scrollPane.getVerticalScrollBar();
    AdjustmentListener downScroller = new AdjustmentListener() {
        @Override
        public void adjustmentValueChanged(AdjustmentEvent e) {
            Adjustable adjustable = e.getAdjustable();
            adjustable.setValue(adjustable.getMaximum());
            verticalBar.removeAdjustmentListener(this);
        }
    };
    verticalBar.addAdjustmentListener(downScroller);
}
Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
17

Instead of setViewPosition(), I usually use scrollRectToVisible(), described in How to Use Scroll Panes. You could use the result of an appropriate label's getBounds() for the required Rectangle.

Addendum: @Matt notes in another answer, "If you use the following code after you output text to the text area in the scrollpane, it will automatically focus on the bottom of the text area."

In the particular case of a JTextComponent, also consider using the setUpdatePolicy() method of DefaultCaret to ALWAYS_UPDATE, illustrated here.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Is it possible to use similar methods like `setViewPosition()` (or the other methods you used) on a regular JPanel? Or is it only possible on a JScrollPane to move the inner of the pane? – Tim Visée Nov 17 '13 at 12:33
  • I found the answer already, the panel or pane should be an instance of JViewport, this allows positioning of the content of the panel. – Tim Visée Nov 17 '13 at 13:29
  • Answer with code using `scrollRectToVisible()`: http://stackoverflow.com/a/6132046/1143274 This worked for me, while `((DefaultCaret)this.jtextpane.getCaret()).setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);` initially seemed to work, but under some conditions I noticed it wasn't scrolling to the bottom. It's hard to tell what those conditions are. – Evgeni Sergeev Dec 30 '13 at 09:57
  • @EvgeniSergeev: Verify that you always update on the [*event dispatch thread*](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html). – trashgod Dec 30 '13 at 10:01
  • @trashgod Yes it always was. – Evgeni Sergeev Jan 31 '14 at 11:15
15

None of the answers worked for me. For some reason my JScrollPane was not scrolling to the very bottom, even if I revalidated everything.

This is what worked for me:

SwingUtilities.invokeLater(() -> {
        JScrollBar bar = scroll.getVerticalScrollBar();
        bar.setValue(bar.getMaximum());
});
David Rochin
  • 317
  • 3
  • 11
1

If your JScrollPane only contains a JTextArea then:

JScrollPane.getViewport().setViewPosition(new Point(0,JTextArea.getDocument().getLength()));
Stefan Stoichev
  • 4,615
  • 3
  • 31
  • 51
Gonzalo
  • 11
  • 1
0
// Scroll to bottom of a JScrollPane containing a list of Strings.

JScrollPane      scrollPane;
DefaultListModel listModel;
JList            list;

listModel = new DefaultListModel();

list = new JList(listModel);
list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
list.setLayoutOrientation(JList.VERTICAL);
list.setVisibleRowCount(-1); // -1 = display max items in space available

scrollPane = new JScrollPane(list);
scrollPane.setPreferredSize(new Dimension(200, 50));

// Append text entries onto the text scroll pane.
listModel.addElement("text entry one");
listModel.addElement("text entry two");
listModel.addElement("text entry three");
listModel.addElement("text entry four");

// Get the index of the last entry appended onto the list, then
// select it, and scroll to ensure it is visible in the vewiport.
int lastNdx = listModel.getSize() - 1;
list.setSelectedIndex(lastNdx);
list.ensureIndexIsVisible(lastNdx);

JPanel panel = new JPanel();
panel.add(scrollPane);
gghptg
  • 1
0

I wanted to contribute my findings to this question, since I needed an answer for it today, and none of the solutions here worked for me, but I did finally find one that did. I had a JList object wrapped in a JScrollPane which I wanted to scroll to the last item after all elements had been added to a DefaultListModel. It essentially works like this:

JList list = new JList();
DefaultListModel listModel = new DefaultListModel();
JScrollPane listScroller = new JScrollPane(list);

public void populateList()
{
     //populate the JList with desired items...
     list.ensureIndexIsVisible(listModel.indexOf(listModel.lastElement()));
}

I tried all of the solutions listed here but none seemed to have any effect. I found this one while experimenting, and it works perfectly. Thought I'd leave it here in the case it might help someone else.

0
ScrollBar vertical = scrollPane.getVerticalScrollBar();
vertical.setValue( vertical.getMaximum() - 1 );

just set value to: vertical.getMaximum() - 1

pereca
  • 75
  • 7
-1

I have tried several method. Some use bar.setValue(bar.getMaximum()), but the maximum is always 100 while the range of bar will be much more than 100. Some use textArea, but it is not suitable for that there is only a JTable in the JScrollPane. And I thing about a method, maybe stupid but effective.

JScrollBar bar=jsp.getVerticalScrollBar();
int x=bar.getValue();
for(int i=100;i<2000000000;i+=100)
{
    bar.setValue(i);
    if(x==bar.getValue())
        break;
    x=bar.getValue();
}
GAOLE LI
  • 1
  • 1
-2

If you look at the JTextArea documentation

public void select(int selectionStart, int selectionEnd)

Selects the text between the specified start and end positions. This method sets the start and end positions of the selected text, enforcing the restriction that the start position must be greater than or equal to zero. The end position must be greater than or equal to the start position, and less than or equal to the length of the text component's text.

If the caller supplies values that are inconsistent or out of bounds, the method enforces these constraints silently, and without failure. Specifically, if the start position or end position is greater than the length of the text, it is reset to equal the text length. If the start position is less than zero, it is reset to zero, and if the end position is less than the start position, it is reset to the start position.

So the simple solution is jTextArea.select(Integer.MAX_VALUE, 0); and let Java sort it out!