Search code examples
javaswingjpaneljscrollpanepreferredsize

JPanel of larger size than the Scrollpane


I have implemented a dialog in which I add a number of checkboxes in a JPanel that also has a scroll bar. The number of check boxes isn't fixed for each run of my program but I know it before I create the dialog.

Unfortunately when I have a large number of checkboxes to add the dialog looks like this:

enter image description here

My code is:

        JPanel listPanel = new JPanel(new GridLayout(files.size() + 2, 1));

        for (int i = 0; i < files.size(); i++) {
            listPanel.add(files.get(i));
        }

        listPanel.setPreferredSize(new Dimension(600, 1400));
        JScrollPane sp =
                new JScrollPane(listPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                        JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        sp.setPreferredSize(new Dimension(300, 200));

        mainPanel.add(sp, BorderLayout.CENTER);
        mainPanel.add(buttonPane, BorderLayout.EAST);

        getContentPane().setLayout(new FlowLayout());

        getContentPane().add(mainPanel);

        pack();
        setResizable(false);

How can I just set the size of the Panel inside the Scrollpane to a larger size without affecting the frame?


Solution

  • Avoid using setPreferredSize, let the layout managers decide the sizes they actually need...

    Updated with example

    The JViewport of the scroll pane is using the preferred size of the listPane to determine how much viewable space it needs, which is a lot...

    In order to overcome this, you need to provide a better hint to the view port as to the viewable size your component would actually like to be. The preferredSize of the component isn't going to work here, you need to supply preferredScrollableViewportSize from the Scrollable interface

    enter image description here

    public class BadList {
    
        public static void main(String[] args) {
            new BadList();
        }
    
        public BadList() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            public TestPane() {
    
                List<String> files = new ArrayList<>(2000);
                for (int index = 0; index < 2000; index++) {
                    files.add(Integer.toString(index));
                }
    
                ListPane listPanel = new ListPane();
                listPanel.setLayout(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.weightx = 1;
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.gridwidth = GridBagConstraints.REMAINDER;
    
                for (String file : files) {
                    listPanel.add(new JCheckBox(file), gbc);
                }
    
                JScrollPane sp = new JScrollPane(listPanel);
    
                JPanel mainPanel = new JPanel(new BorderLayout());
                JPanel buttonPane = new JPanel(new GridBagLayout());
                buttonPane.add(new JButton("Select All"), gbc);
                buttonPane.add(new JButton("Deselect All"), gbc);
                gbc.weighty = 1;
                buttonPane.add(new JPanel(), gbc);
    
                mainPanel.add(sp, BorderLayout.CENTER);
                mainPanel.add(buttonPane, BorderLayout.EAST);
    
                setLayout(new BorderLayout());
    
                add(mainPanel);
            }
        }
    
        public class ListPane extends JPanel implements Scrollable {
    
            @Override
            public Dimension getPreferredScrollableViewportSize() {
                return new Dimension(200, 200);
            }
    
            @Override
            public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
                return 128;
            }
    
            @Override
            public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
                return 128;
            }
    
            @Override
            public boolean getScrollableTracksViewportWidth() {
                return false;
            }
    
            @Override
            public boolean getScrollableTracksViewportHeight() {
                return false;
            }
    
        }
    }
    

    nb- I've used some arbitrary magic numbers in this example, you're going to what to provide more realistic values for your need