Search code examples
javaswinglayoutflowlayoutpanelboxlayout

Nested layouts - FlowLayout inside BoxLayout


I have a controlPanel (BoxLayout):

controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));

Now I build two FlowLayout and add them to the contolPanel panel:

JPanel fromDatePanel = new JPanel(new FlowLayout());
JPanel untilDatePanel = new JPanel(new FlowLayout());

fromDatePanel.add(new JLabel("From - "));
fromDatePanel.add(new JButton("..."));
untilDatePanel.add(new JLabel("Until - "));
untilDatePanel.add(new JButton("..."));

controlPanel.add(fromDatePanel);
controlPanel.add(untilDatePanel);

I'm getting this:

enter image description here

Why it creates a gap between the layouts? If I insert a JButton for example, it works fine (It inserts them with no gap).

How can I remove the gap between the two FlowLayout? (So it'll be like the blue gap)


Solution

  • You can achieve a vertical layout (vertically centered) using GridBagLayout (and use the GridBagConstraint with a gridwidth=REMAINDER):

    import java.awt.FlowLayout;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class TestGridBagLayout {
    
        protected void initUI() {
            JFrame frame = new JFrame();
            JPanel controlPanel = (JPanel) frame.getContentPane();
            controlPanel.setLayout(new GridBagLayout());
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
    
            JPanel fromDatePanel = new JPanel(new FlowLayout());
            JPanel untilDatePanel = new JPanel(new FlowLayout());
    
            fromDatePanel.add(new JLabel("From - "));
            fromDatePanel.add(new JButton("..."));
            untilDatePanel.add(new JLabel("Until - "));
            untilDatePanel.add(new JButton("..."));
    
            controlPanel.add(fromDatePanel, gbc);
            controlPanel.add(untilDatePanel, gbc);
            frame.setSize(600, 600);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    TestGridBagLayout testMultiplePanels = new TestGridBagLayout();
                    testMultiplePanels.initUI();
    
                }
            });
        }
    
    }
    

    Regarding the difference between JButton and JPanel when added to a BoxLayout, this is due to the differences of implementation of the getMaximumSize() which is taken into account by the BoxLayout. JButton returns the preferred size while JPanel will return null which is interpreted by the BoxLayout as infinite dimensions.

    If you want to keep your BoxLayout, you could override JPanel.getMaximumSize() and return getPreferredSize():

    import java.awt.Dimension;
    import java.awt.FlowLayout;
    
    import javax.swing.BoxLayout;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class TestGridBagLayout {
    
        protected void initUI() {
            JFrame frame = new JFrame();
            JPanel controlPanel = (JPanel) frame.getContentPane();
            controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JPanel fromDatePanel = new JPanel(new FlowLayout()) {
                @Override
                public Dimension getMaximumSize() {
                    return getPreferredSize();
                }
            };
            JPanel untilDatePanel = new JPanel(new FlowLayout()) {
                @Override
                public Dimension getMaximumSize() {
                    return super.getMaximumSize();
                }
            };
    
            fromDatePanel.add(new JLabel("From - "));
            fromDatePanel.add(new JButton("..."));
            untilDatePanel.add(new JLabel("Until - "));
            untilDatePanel.add(new JButton("..."));
    
            controlPanel.add(fromDatePanel);
            controlPanel.add(untilDatePanel);
            frame.setSize(600, 600);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    TestGridBagLayout testMultiplePanels = new TestGridBagLayout();
                    testMultiplePanels.initUI();
    
                }
            });
        }
    
    }