Search code examples
javaswinglayout-managerflowlayout

FlowLayout wrapping is not working in nested JPanel


I am trying to organise a frame according to the following pattern:

 _______________________
|  ___________________  |
| | +   +   +   +   + | |
| | +   +             | |
| |___________________| |
|  ___________          |
| | +   +   + |         |
| |___________|         |
|_______________________|

Here are my requirements:

  • I do not know at compile time how many components I will have in each nested JPanel
  • The user should be able to resize the window
  • The nested JPanel(s) should extend vertically if there not enough space to display all the components on one line
  • The nested JPanel(s) should not extend vertically if there is available space (i.e. I do not want to have vertical white spaces in the nested JPanel(s), but it's fine if there are some in the main frame)

For the nested JPanel(s), it seems to me that a FlowLayout is the best solution, and I tried to used a BoxLayout and a GridBagLayout for organising my panels, but without success: the internal components never wrap.

Here is my code:

public class Example {

private static final int NB1 = 5;
private static final int NB2 = 13;
private static final int NB3 = 15;

public static void main(String[] args) {

    JFrame f = new JFrame();
    f.setTitle("Example");
    f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    f.setSize(300,600);

    JPanel p1 = new JPanel(new FlowLayout(FlowLayout.CENTER));
    p1.setBorder(new TitledBorder(new LineBorder(Color.BLACK), "JPanel 1"));
    for(int i=0; i<NB1; i++)
        p1.add(new JButton("Button " + i));

    JPanel p2 = new JPanel(new FlowLayout(FlowLayout.CENTER));
    p2.setBorder(new TitledBorder(new LineBorder(Color.BLACK), "JPanel 2"));
    for(int i=NB1; i<NB2; i++)
        p2.add(new JButton("Button " + i));

    JPanel p3 = new JPanel(new FlowLayout(FlowLayout.CENTER));
    p3.setBorder(new TitledBorder(new LineBorder(Color.BLACK), "JPanel 3"));
    for(int i=NB2; i<NB3; i++)
        p3.add(new JButton("Button " + i));

//  JPanel global = new JPanel(new GridBagLayout());
//  GridBagConstraints gbc = new GridBagConstraints();
//  gbc.gridy = 0;
//  global.add(p1, gbc);
//  gbc.gridy++;
//  global.add(p2, gbc);
//  gbc.gridy++;
//  global.add(p3, gbc);

    JPanel global = new JPanel();
    Box vb = Box.createVerticalBox();
    vb.add(p1);
    vb.add(Box.createVerticalStrut(10));
    vb.add(p2);
    vb.add(Box.createVerticalStrut(10));
    vb.add(p3);
    global.add(vb);

    f.add(global);
    f.setVisible(true);

}

}

If you have any idea how to proceed...

Thank you!


Solution

  • For you layout you need to make sure that the panel will fill the horizontal space when the frame is resized.

    However, you still can't use the FlowLayout because FlowLayout does not recalculate the preferred size of the panel as the components wrap. Instead you need to use the Wrap Layout which will recalculate the preferred size.

    Here is the updated example with "panel 2" using the WrapLayout and the GridBagLayout changes:

    import java.awt.*;
    import javax.swing.*;
    import javax.swing.border.*;
    
    public class Example2 {
    
    private static final int NB1 = 5;
    private static final int NB2 = 13;
    private static final int NB3 = 15;
    
    public static void main(String[] args) {
    
        JFrame f = new JFrame();
        f.setTitle("Example2");
        f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    
        JPanel p1 = new JPanel(new FlowLayout(FlowLayout.CENTER));
        p1.setBorder(new TitledBorder(new LineBorder(Color.BLACK), "JPanel 1"));
        for(int i=0; i<NB1; i++)
            p1.add(new JButton("Button " + i));
    
    //    JPanel p2 = new JPanel(new FlowLayout(FlowLayout.CENTER));
        JPanel p2 = new JPanel(new WrapLayout(FlowLayout.CENTER));
        p2.setBorder(new TitledBorder(new LineBorder(Color.BLACK), "JPanel 2"));
        for(int i=NB1; i<NB2; i++)
            p2.add(new JButton("Button " + i));
    
        JPanel p3 = new JPanel(new FlowLayout(FlowLayout.CENTER));
        p3.setBorder(new TitledBorder(new LineBorder(Color.BLACK), "JPanel 3"));
        for(int i=NB2; i<NB3; i++)
            p3.add(new JButton("Button " + i));
    
        JPanel global = new JPanel(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL; // added
        gbc.weightx = 1.0f; // added
        gbc.gridy = 0;
        global.add(p1, gbc);
        gbc.gridy++;
        global.add(p2, gbc);
        gbc.gridy++;
        global.add(p3, gbc);
    
        f.add(global);
        f.pack();
        f.setVisible(true);
    
    }
    
    }