I’m using a scrollpane set to use a custom scrollbar and among other things I wanted to install a listener on the thumb of the scrollbar so that whenever the mouse enters the thumb area it could change color or border. I searched BasicScrollBarUI (which is extended by my custom scrollbar UI) and found the method installListeners(), so I overrode it and made it call one more listener for the thumb area.
SSCCE:
public class TestScrollBar extends JFrame {
public TestScrollBar() {
JPanel innerPanel = new JPanel();
innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.Y_AXIS));
for (int i=1; i<=10; i++) {
innerPanel.add(new JLabel("Label "+i));
innerPanel.add(Box.createRigidArea(new Dimension(0, 20)));
}
JScrollPane scrollPane = new JScrollPane(innerPanel);
scrollPane.setPreferredSize(new Dimension(innerPanel.getPreferredSize().width, innerPanel.getPreferredSize().height/2));
scrollPane.getVerticalScrollBar().setUI(new CustomScrollBarUI());
this.setLayout(new BorderLayout());
this.add(Box.createRigidArea(new Dimension(0, 20)), BorderLayout.NORTH);
this.add(Box.createRigidArea(new Dimension(0, 20)), BorderLayout.SOUTH);
this.add(Box.createRigidArea(new Dimension(20, 0)), BorderLayout.EAST);
this.add(Box.createRigidArea(new Dimension(20, 0)), BorderLayout.WEST);
this.add(scrollPane, BorderLayout.CENTER);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
TestScrollBar frame = new TestScrollBar();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
And a simple custom UI for the scrollbar:
public class CustomScrollBarUI extends BasicScrollBarUI {
@Override
protected void installListeners() {
super.installListeners();
scrollbar.addMouseListener(new CustomListener());
}
protected class CustomListener extends MouseAdapter {
public void mouseEntered(MouseEvent e) {
if (getThumbBounds().contains(e.getX(), e.getY())) {
System.out.println("THUMB");
}
}
}
}
The listener works fine when the mouse enters the thumb area from the right or left but it doesn’t always work for the top or bottom side. For these sides it only works when the thumb is touching one of the buttons. For example when the scrollbar is at the very top, the listener works only (except for the right-left side) when the mouse enters the thumb area from the top button but not from below. The opposite thing happens when the scrollbar touches the decrease button (only works for right/left/bottom sides but not for top). When the thumb is somewhere between without touching any of the buttons then only the left-right sides work. You can see all this if you run the code.
I also tried to use one of the existing listeners (instead of creating my own), BasicScrollBarUI.TrackListener, where I added a method to listen only for the thumb area, but the results where exactly the same:
public class CustomScrollBarUI extends BasicScrollBarUI {
@Override
protected TrackListener createTrackListener(){
return new TrackListener();
}
protected class TrackListener extends BasicScrollBarUI.TrackListener {
public void mouseEntered(MouseEvent e) {
currentMouseX = e.getX();
currentMouseY = e.getY();
if (getThumbBounds().contains(currentMouseX, currentMouseY)) {
System.out.println("THUMB");
}
}
}
}
The problem is that you listen for the scrollbar, which means that it is the Thumb and the area where the Thumb can move (the track). Therefore your test condition in the mouseEntered method is not always true. For example, let’s say the thumb is at the top of a vertical scrollbar, entering through the bottom means that you will first enter the scrollbar track and then move the mouse to the thumb area –> no mouse entered event.
Here is a slightly modified version of your code. What it mainly does is also listen for mouse “move” event (by adding your listener as a MouseMotionListener) and it works as expected: