Search code examples
javaswingjscrollpanelayout-managergrid-layout

Swing - GridLayout components not filling up all available space


I've done a search of StackOverflow but all of the questions seem to ask the exact opposite of my problem.

I'm writing code that dynamically adds JLabels to a JPanel with a GridLayout, all contained within a JScrollPane. Here's an SSCCE:

private JFrame frame;
private JPanel panel;
static Test window;
private JScrollPane scrollPane;

public static void main(final String[] args) {
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            try {
                window = new Test();
                window.frame.setVisible(true);
            } catch (final Exception e) {
                e.printStackTrace();
            }
        }
    });

    for (int i = 0; i < 100; i++) {
        try {
            EventQueue.invokeAndWait (new Runnable() {
                @Override
                public void run() {
                    final JLabel label = new JLabel("Test");
                    label.setSize(160, 40);
                    label.setHorizontalAlignment(SwingConstants.CENTER);
                    // Finalise GUI
                    window.panel.add(label);
                    window.panel.revalidate();
                    window.panel.repaint();
                    try {
                        Thread.sleep(100);
                    } catch (final Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        } catch (final Exception e) {
            e.printStackTrace();
        }
    }
}

public Test() {
    initialize();
}

private void initialize() {
    frame = new JFrame();
    frame.setBounds(100, 100, 500, 200);
    frame.getContentPane().setLayout(null);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    panel = new JPanel();
    panel.setLayout(new GridLayout(0, 3));

    final JPanel outerPanel = new JPanel();
    outerPanel.setLayout(new FlowLayout());
    outerPanel.add(panel);

    scrollPane = new JScrollPane(outerPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    scrollPane.setBounds(12, 0, 460, 160);
    frame.getContentPane().add(scrollPane);
}

From my understanding, in GridLayouts, "each component takes all the available space within its cell." Yet here, the JLabels don't actually take up all the available space in the JPanel.

I'm not sure where my mistake is. Is it in the GridLayout or in the surrounding Components not giving the GridLayout enough space?

Thanks all.


Solution

  • Your JLabels are taking all available space. It's your JPanel that's small. Test it yourself with a Border:

        panel = new JPanel();
        panel.setLayout(new GridLayout(0, 3));
    
        // **** add this to see ****
        panel.setBorder(BorderFactory.createLineBorder(Color.BLUE));
    

    If you want the labels to fill out the top, then use a different layout for the outer panel. Note changes marked with the // !! comment:

        final JPanel outerPanel = new JPanel();
        // !! outerPanel.setLayout(new FlowLayout());
        outerPanel.setLayout(new BorderLayout()); // !!
        // !! outerPanel.add(panel);
        outerPanel.add(panel, BorderLayout.PAGE_START); // !!
    

    Also that Thread.sleep(...) is dangerous code in a Swing GUI. If you want a delay in adding components, use the best Swing tool for the job: a Swing Timer. For example

        final int timerDelay = 100;
        final int maxLabelCount = 100; 
        new Timer(timerDelay, new ActionListener() {
            private int labelCount = 0;
            @Override
            public void actionPerformed(ActionEvent evt) {
                if (labelCount < maxLabelCount) {
                    final JLabel label = new JLabel("Test");
                    // !! label.setSize(160, 40); // NO!
                    label.setHorizontalAlignment(SwingConstants.CENTER);
                    // Finalise GUI
                    window.panel.add(label);
                    window.panel.revalidate();
                    window.panel.repaint();
                } else {
                    // stop this timer
                    ((Timer) evt.getSource()).stop();
                }
                labelCount++;
            }
        }).start();