How to make JScrollPane scroll 1 line per mouse wheel step?
Asked Answered
E

2

6

I have a JScrollPane whose content pane is a JXList. When I use the mouse wheel on the list, the list steps three (3) items at a time. This also works for a table, regardless of row height. How can I change this so that - regardless of platform - for both list and table the scroll distance is exactly 1 item? Setting block increment doesn't cut it because some rows in the table have a different height.

Eyelet answered 9/7, 2012 at 14:53 Comment(4)
@trashgod hadn't looked for a while - the wheel really seems to map to the unitIncrement somehow, and does handle individual row heights, so was wrong with my doubt. Tweaking the scrollable implementation might help, after all :-)Market
See camickr's Mouse Wheel ControllerStoat
@Stoat hach .. forgot that one :-) Definitely the way to go, consider making your comment an answer, to grab some real votes.Market
@kleopatra, Thanks for the supplementary explanation. I'm leaving this to you.Stoat
F
6

Out of pure interest (and a little boredom) I created a working example:

/**
 * Scrolls exactly one Item a time. Works for JTable and JList.
 *
 * @author Lukas Knuth
 * @version 1.0
 */
public class Main {

    private JTable table;
    private JList list;
    private JFrame frame;

    private final String[] data;

    /**
     * This is where the magic with the "just one item per scroll" happens!
     */
    private final AdjustmentListener singleItemScroll = new AdjustmentListener() {
        @Override
        public void adjustmentValueChanged(AdjustmentEvent e) {
            // The user scrolled the List (using the bar, mouse wheel or something else):
            if (e.getAdjustmentType() == AdjustmentEvent.TRACK){
                // Jump to the next "block" (which is a row".
                e.getAdjustable().setBlockIncrement(1);
            }
        }
    };

    public Main(){
        // Place some random data:
        Random rnd = new Random();
        data = new String[120];
        for (int i = 0; i < data.length; i++)
            data[i] = "Set "+i+" for: "+rnd.nextInt();
        for (int i = 0; i < data.length; i+=10)
            data[i] = "<html>"+data[i]+"<br>Spacer!</html>";
        // Create the GUI:
        setupGui();
        // Show:
        frame.pack();
        frame.setVisible(true);
    }

    private void setupGui(){
        frame = new JFrame("Single Scroll in Swing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        frame.add(split);

        // Add Data to the table:
        table = new JTable(new AbstractTableModel() {
            @Override
            public int getRowCount() {
                return data.length;
            }

            @Override
            public int getColumnCount() {
                return 1;
            }

            @Override
            public Object getValueAt(int rowIndex, int columnIndex) {
                return data[rowIndex];
            }
        });
        for (int i = 0; i < data.length; i+=10)
            table.setRowHeight(i, 30);
        JScrollPane scroll = new JScrollPane(table);
        // Add out custom AdjustmentListener to jump only one row per scroll:
        scroll.getVerticalScrollBar().addAdjustmentListener(singleItemScroll);
        split.add(scroll);

        list = new JList<String>(data);
        scroll = new JScrollPane(list);
        // Add out custom AdjustmentListener to jump only one row per scroll:
        scroll.getVerticalScrollBar().addAdjustmentListener(singleItemScroll);
        split.add(scroll);
    }

    public static void main(String[] agrs){
        new Main();
    }
}

The real magic is done in the custom AdjustmentListener, where we go and increase the current "scroll-position" by one single block per time. This works up and down and with different row-sizes, as shown in the example.


As @kleopatra mentioned in the comments, you can also use a MouseWheelListener to only redefine the behavior of the mouse-wheel.

See the official tutorial here.

Fulgurous answered 9/7, 2012 at 16:3 Comment(7)
This is pretty damn awesome. Thanks a million! I mean, sure, there /could/ be a .setScrollIncrement(int i) somewhere, but this way it works, too. Just a short aside: This code makes the scroll bar's page feature (clicking on the bar outside of the handle) scroll baby steps, which isn't super strange considering your code sets the block increment to 1 "globally". Is there any way to just change the mouse wheel step size? Also, can you recommend a book on Swing?Eyelet
Ah, gotta comment a bit more. I was curious why setBlockIncrement(1) has to be called each time the AdjustmentListener is called, so I deactivated the listener and called setBlockIncrement(1) once at the time of creating the scroll pane. Behold! This works, too, with the same unwanted side-effect. I can't remember what the effect of this was at my office machine so I'll test this at work tomorrow. Sometimes I don't like Swing. ;-)Eyelet
@Eyelet for swing, just read the tutorials provided by sun/oracle. I didn't read a dedicated book for that. Swing is pretty powerful, but it's also not the smallest GUI framework.Fulgurous
-1 for going low-level when there is a high-level approach ;-) The scrollPane's wheelListener is the location which decides about the scroll amount: the default does so by scrolling the number of units per notch as set by the OS control panel. That's the location to change, if required: simply map a notch to only one unit in a custom MouseWheelListener.Market
@Market I wasn't aware of that listener. I updated my answer accordingly, thanks for pointing it out.Fulgurous
@Market What "OS control panel" do you mean? jcontrol? Because I couldn't find that setting there.Eyelet
@Eyelet sorry for being unclear: I meant the setting done natively in the OS, in win that used to be called "control panel", so nothing to do with javaMarket
L
2

Too much code for a simple explanation:

JLabel lblNewLabel = new JLabel("a lot of line of text...");
JScrollPane jsp = new JScrollPane(lblNewLabel);
jsp.getVerticalScrollBar().setUnitIncrement(10); //the bigger the number, more scrolling
frame.getContentPane().add(jsp, BorderLayout.CENTER);
Leadership answered 11/9, 2018 at 16:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.