The Anonian
JScrollPane & JPanels

Recently, I wanted to use the arrow keys on a keyboard to navigate a JScrollPane. That’s easy enough: Java’s JScrollPane already binds these keystrokes to the scroll bar. However, this only works when the scroll pane has focus. Once it loses focus, it is no longer notified of keystroke events.

Since I have two separate scroll panes that I wanted to be keyboard scrollable, though not at the same time, this actually worked to my advantage. The user will be able to select which scroll pane to navigate by a simple mouse click. The scroll pane that the user clicks inside of will be given focus and become controllable via the keyboard.

There’s only one problem: Clicking on a component inside of the scroll pane does not give the containing scroll pane focus. Instead, the child component clicked on gains focus.

Global Event Listeners to the Rescue!

By using a Global Event Listener, we can manually check whether or not the mouse click event was on the scroll pane or one its children components. We can then force focus to be given to the containing scroll pane.

// Filter to only catch mouse events
long eventMask = AWTEvent.MOUSE_EVENT_MASK;

// Add Global Event Listener
Toolkit.getDefaultToolkit().addAWTEventListener( new AWTEventListener()
{
    @Override
    public void eventDispatched(AWTEvent e)
    {

        // Only take action on Mouse Click events on Components
        if (e.getID() == MouseEvent.MOUSE_CLICKED 
                && e.getSource() instanceof Component)
        {

            Component src = (Component)e.getSource();

            // Request focus if child component was clicked on
            if (SwingUtilities.isDescendingFrom(src, scrollPane))
            {
                scrollPane.requestFocusInWindow();
            }

        }
        
    }
}, eventMask);

Personally, I found the scrolling painfully slow. Increasing the unit increment was a quick solve.

JScrollBar vertical = scrollPane.getVerticalScrollBar();
vertical.setUnitIncrement(10);

If you would like to customize the keys used for scrolling, or if your version of Java does not automatically handle arrow key scrolling, Key Bindings are the way to go.

JScrollBar vertical = scrollPane.getVerticalScrollBar();
InputMap im = vertical.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);

// Scroll down
im.put(KeyStroke.getKeyStroke("DOWN"), "positiveUnitIncrement"); 

// Scroll up
im.put(KeyStroke.getKeyStroke("UP"), "negativeUnitIncrement");

Sources

https://stackoverflow.com/questions/4298582/jscrollpane-scrolling-with-arrow-keys

https://stackoverflow.com/questions/27485660/set-focus-in-a-jpanel?rq=1

https://tips4java.wordpress.com/2008/10/10/key-bindings/

https://tips4java.wordpress.com/2009/08/30/global-event-listeners/

https://docs.oracle.com/javase/7/docs/api/java/awt/event/AWTEventListener.html

https://docs.oracle.com/javase/7/docs/api/java/awt/event/MouseEvent.html

Photo from https://gratisography.com/photo/author/ryan-mcguire/

Discover more from Hunter Schoonover

Subscribe now to keep reading and get access to the full archive.

Continue reading