Question

I've got the next code:

    listModel = new DefaultListModel();
    listModel.addElement(dateFormat.format(new Date()) + ": Msg1");
    messageList = new JList(listModel);
    messageList.setLayoutOrientation(JList.VERTICAL);

    messageScrollList = new JScrollPane(messageList);
    messageScrollList.setPreferredSize(new Dimension(500, 200));

    messageScrollList.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {  
        public void adjustmentValueChanged(AdjustmentEvent e) {  
            e.getAdjustable().setValue(e.getAdjustable().getMaximum());  
        }
    }); 

It auto scrolls down. But, if I try to scroll back up to re-read a message, it forces a scroll down. How can I fix this?

Was it helpful?

Solution

When adding a new message, invoke scrollRectToVisible() on the JList using a Rectangle having the same dimensions as your message pane's preferred size. Given a vertical orientation, it may be convenient to make the preferred size of the JScrollPane's JViewport an integral multiple of the message pane's height. See also: How to Use Scroll Panes.

Addendum: This compelling discussion of Text Area Scrolling may be helpful, too.

OTHER TIPS

I found this really useful: http://forums.sun.com/thread.jspa?threadID=623669 (post by 'inopia')
It works perfectly

As he says: "The problem here is that it can become a bit difficult to find an event that fires after both the ListModel, JList and JScrollPane have been updated."

this.list = blah blah... 
this.list.setSelectedValue(whatever);   
final JScrollPane sp = new JScrollPane(this.list); // needs to be after the parent is the sp 
this.list.ensureIndexIsVisible(this.list.getSelectedIndex());

@question enquirer. Please change your code from

messageScrollList.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {  
        public void adjustmentValueChanged(AdjustmentEvent e) {  
            e.getAdjustable().setValue(e.getAdjustable().getMaximum());  
        }
    });

to

messageScrollList.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {  
        public void adjustmentValueChanged(AdjustmentEvent e) {  
            e.getAdjustable().setValue(e.getAdjustable().getValue());  
        }
    }); 

That is change getMaximum() to getValue() Then it works as required. getMaximum() takes the scroller to its maximum; while getValue() takes it wherever event happens.

You can avoid using this piece of code altogether if you put one line

messageScrollList.setViewportView(messageList);

That works like add(component) for jList and gets its inherent behaviour.

To auto scroll I am using a very simple concept of static variable and list.makeVisible(int index)

    public class autoScrollExample
    {
     static int index=0;
     private void someFunction()
     /*   
     * 
     */
     list1.add("element1");
     list1.makeVisible(index++);
     /*
     *
     */

     private void someOtherFunction()
     /*   
     * 
     */
     list1.add("element2");
     list1.makeVisible(index++);
     /*
     *
     */


    }

I used JScrollPane with JTextArea. I avoided force scroll down by scrolling only when JScrollBar max value changed:

        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();
                });

I suppose, there is better solution to filter events without extra variables, and would appreciate if somebody post it.

I think what you want to do is have it scroll down when you add stuff to your messageList, rather then on adjustment. So your code could look like this:

Adjustable sb = messageScrollList.getVerticalScrollBar()
boolean onBottom = sb.getValue() == sb.getMaximum();
//
// add your message to the JList.
//
if(onBottom)  sb.setValue(sb.getMaximum());

Otherwise you would need to tell if the adjustment was caused by a model change, or by the mouse, and looking through the API docs I'm not sure if there's a way to do that easily. Although you could see if the AdjustmentEvent.getAdjustmentType() returns different values in those cases, if that's true then you could just have an if statement in your anonymous inner class.

Another thing you could try would be to have a boolean variable somewhere that gets set when you add something to the list. Then, in your handler you check to see if the variable is set. If so, you do the adjustment (and unset the variable) otherwise, you ignore it. That way, there will be only one scroll-down per item being added to the list.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top