How do you add a mouseListener to a jscrollbar?

1k views Asked by At

I have mouseEntered and mouseExited events that cause jPanel content to change which changes the size of the jPanel. Upon mouseEntered, the content grows and is scrollable. Upon mouseExited, the content shrinks and is not scrollable.

The problem is that hovering the mouse over the scrollbar triggers the mouseExited event from the jPanel, so when the user wants to drag the scrollbar's knob, it disappears when they go to click on it.

If I could add a mouseListener to the scrollbar itself, I imagine I could keep the content from shrinking. But I've never added a mouseListener to a scrollbar. How can I trigger an event on mouseEntered/Exited over a scrollbar?

2

There are 2 answers

0
hepcat72 On BEST ANSWER

Yes, you can attach a mouseListener to a scrollbar. It's necessary when your content changes on mouseEntered/mouseExited WRT a jPanel because hovering over the jPanel's scrollbar triggers mouseExited. You must implement 2 aspects in order to keep the content as if you were hovered over the jPanel: mouseEntered/mouseExited and mouseClicked/mouseReleased. The reason for this is because the mouse can hover off the scrollbar when the scrollbar's knob is being dragged, and you don't want the content to change mid-scroll.

All you need to do is, add the listener directly to the scrollbar:

myscrollbar.addMouseListener(new MouseAdapter() {

        @Override
        public void mouseEntered(MouseEvent e) {
            if(overScrollOffTimer != null) {
                overScrollOffTimer.stop();
                overScrollOffTimer = null;
            }
            mymodel.setOverScrollbar(true);
        }

        @Override
        public void mouseExited(MouseEvent e) {
            if(overScrollOffTimer == null) {
                overScrollOffTimer = new Timer(250,
                        overScrollOffListener);
                overScrollOffTimer.start();
            }
        }

        @Override
        public void mousePressed(MouseEvent e) {
            if(activeScrollOffTimer != null) {
                activeScrollOffTimer.stop();
                activeScrollOffTimer = null;
            }
            mymodel.setBeingScrolled(true);
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            if(activeScrollOffTimer == null) {
                activeScrollOffTimer =
                    new Timer(250,
                              activeScrollOffListener);
                activeScrollOffTimer.start();
            }
        }
    });

In my mouseListener, I added 2 timers that initiated the change of the jPanel content (after 250ms) (plus a third timer for the jPanel hover). Without the timers, the content can visually hiccup as you hover from the panel to the scrollbar or back. One timer started on mouseExited from the scrollbar. The other started when mouseReleased the scrollbar knob. I kept track of whether either the mouse was over the scrollbar, moving the scrollbar, and whether the mouse was over the jPanel. If any of those things was true, I displayed my large content. Otherwise, if they were all false, I would draw my small content.

Here is an example of the timers:

private javax.swing.Timer overScrollOffTimer;
ActionListener overScrollOffListener = new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent evt) {
        if(evt.getSource() == overScrollOffTimer) {
            /* Stop timer */
            overScrollOffTimer.stop();
            overScrollOffTimer = null;

            mymodel.setOverScrollbar(false);
            mymodel.notifyObservers();
            revalidate();
            repaint();
        }
    }
};

private javax.swing.Timer activeScrollOffTimer;
ActionListener activeScrollOffListener = new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent evt) {
        if (evt.getSource() == activeScrollOffTimer) {
            /* Stop timer */
            activeScrollOffTimer.stop();
            activeScrollOffTimer = null;

            mymodel.setBeingScrolled(false);
            mymodel.notifyObservers();
            revalidate();
            repaint();
        }
    }
};

I have not included the timer or mouse listener for the jPanel, but it's a similar endeavor without the clicked/released methods.

Note, I tracked the status of the hover and the scrolling in the model because other jPanels display different content based on this hover interaction as well and they all have access to the model.

BTW, I used the terms "OffTimer", "OffListener", etc. because I'm basically only showing real content on hover over a set of panels. Hovering off any of these panels basically causes the content to disappear. The reason I want the content to disappear is because the content changes as you hover over it and I don't want the user to be confused by static unmoving content and lead them to make incorrect visual conclusions (e.g. associate visually aligned data in 2 panels whose alignment changes based on hover position).

1
FredK On

Why not place the scroll pane inside another JPanel, and add the listener on that external pane instead. Then moving into the scrollbar will not trigger a mouseExited event; it will be triggered only when you go PAST the scrollbar (exiting the outer panel) or outside the scrolled panel in another direction.