Search code examples
javaswinglayout-managerboxlayout

Make cells of BoxLayout keep size and only resize glue in java


I'm trying to make a settings panel and I want to be able to keep seeing what setting belongs to what text. So I put every jlabel with a jcheckbox or jspinner in a jpanel and then I add them to column jpanels. I have 4 column jpanels which are next to each other. In the attached images you can see 2 different width sizes. I want the red areas to be static and the blue areas to resize, because that way you can see what jlabel belongs to what setting. enter image description hereenter image description here

Here I have a SSCCE

import javax.swing.*;
import java.awt.*;

public class SSCCE {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
                e.printStackTrace();
            }
            JFrame jFrame = new JFrame();
            jFrame.setMinimumSize(new Dimension(580, 113));
            jFrame.add(new SettingsPanel());
            jFrame.pack();
            jFrame.setLocationRelativeTo(null);
            jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            jFrame.setVisible(true);
        });
    }

    public static class Setting extends JPanel {
        public String name;
        public JComponent component;

        public Setting(String name, JComponent component) {
            this.name = name;
            this.component = component;
            setLayout(new BorderLayout());
            add(new JLabel(name), BorderLayout.LINE_START);
            add(component, BorderLayout.LINE_END);
        }
    }

    private static class SettingsPanel extends JPanel {

        public SettingsPanel() {
            setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));

            JPanel subPanel = new JPanel(new GridLayout(0, 1));
            subPanel.add(new Setting("Amount of faces", new JSpinner()));
            subPanel.add(new Setting("Amount of vertices", new JSpinner()));
            subPanel.add(new Setting("Amount of textured faces", new JSpinner()));
            add(subPanel);

            add(Box.createHorizontalGlue());

            subPanel = new JPanel(new GridLayout(0, 1));
            subPanel.add(new Setting("Textures", new JCheckBox()));
            subPanel.add(new Setting("Priorities", new JCheckBox()));
            subPanel.add(new Setting("Alpha", new JCheckBox()));
            add(subPanel);

            add(Box.createHorizontalGlue());

            subPanel = new JPanel(new GridLayout(0, 1));
            subPanel.add(new Setting("TSkins", new JCheckBox()));
            subPanel.add(new Setting("VSkins", new JCheckBox()));
            subPanel.add(new Setting("X data", new JSpinner()));
            add(subPanel);

            add(Box.createHorizontalGlue());

            subPanel = new JPanel(new GridLayout(0, 1));
            subPanel.add(new Setting("Y data", new JSpinner()));
            subPanel.add(new Setting("Z data", new JSpinner()));
            subPanel.add(new Setting("Triangle data", new JSpinner()));
            add(subPanel);

            setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.BLACK));
        }
    }
}


EDIT

I applied Camickr's answer and this was the outcomeenter image description here As you can see the jspinners and jcheckboxes are now not in line anymore and it looks a bit messy, but the way it resizes is the functionality I want. Here is the updated SSCCE with Camickr's answer

import javax.swing.*;
import java.awt.*;

public class SSCCE {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
                e.printStackTrace();
            }
            JFrame jFrame = new JFrame();
            jFrame.setMinimumSize(new Dimension(580, 113));
            jFrame.add(new SettingsPanel());
            jFrame.pack();
            jFrame.setLocationRelativeTo(null);
            jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            jFrame.setVisible(true);
        });
    }

    public static class Setting extends JPanel {
        public String name;
        public JComponent component;

        public Setting(String name, JComponent component) {
            this.name = name;
            this.component = component;
            setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
            add(new JLabel(name), BorderLayout.LINE_START);
            add(Box.createRigidArea(new Dimension(50, 0)));
            add(component, BorderLayout.LINE_END);
        }
    }

    private static class SettingsPanel extends JPanel {

        public SettingsPanel() {
            setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));

            JPanel subPanel = new JPanel(new GridLayout(0, 1));
            subPanel.add(new Setting("Amount of faces", new JSpinner()));
            subPanel.add(new Setting("Amount of vertices", new JSpinner()));
            subPanel.add(new Setting("Amount of textured faces", new JSpinner()));
            add(subPanel);

            add(Box.createHorizontalGlue());

            subPanel = new JPanel(new GridLayout(0, 1));
            subPanel.add(new Setting("Textures", new JCheckBox()));
            subPanel.add(new Setting("Priorities", new JCheckBox()));
            subPanel.add(new Setting("Alpha", new JCheckBox()));
            add(subPanel);

            add(Box.createHorizontalGlue());

            subPanel = new JPanel(new GridLayout(0, 1));
            subPanel.add(new Setting("TSkins", new JCheckBox()));
            subPanel.add(new Setting("VSkins", new JCheckBox()));
            subPanel.add(new Setting("X data", new JSpinner()));
            add(subPanel);

            add(Box.createHorizontalGlue());

            subPanel = new JPanel(new GridLayout(0, 1));
            subPanel.add(new Setting("Y data", new JSpinner()));
            subPanel.add(new Setting("Z data", new JSpinner()));
            subPanel.add(new Setting("Triangle data", new JSpinner()));
            add(subPanel);

            setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.BLACK));
        }
    }
}


Solution

  • I want the red areas to be static

    Use the Box.createRigidArea(...) instead of the "glue".

    See the section from the Swing tutorial on Using Invisible Components as Filler for more information.

    Edit:

    Messed up with the details of the layout managers you are using.

    The problem is the SettingPanel. You are using a horizontal BoxLayout. The BoxLayout respects the maximum size of any component added to it. You are adding a JPanel which doesn't have a maximum size, so the size of each panel increases which causes the GridLayout to also increase, causing the gap in the BorderLayout components.

    You want to prevent the panels from increasing in size. Simple change to prove the concept is to add the following when you add all your panels:

    subPanel.setMaximumSize( subPanel.getPreferredSize() );
    add(subPanel);
    

    The better solution is to extend JPanel and override the getMaximumSize() method to return the getPreferredSize() to allow for dynamic changing of the panels preferred size.

    After implementing this suggestion I suggest you remove:

    jFrame.setMinimumSize(new Dimension(580, 113)); 
    

    It is never a good idea to use random numbers. Let the layout manager determine the size.

    When you do this you will see all the component clumped together so I suggest you change:

    //setLayout(new BorderLayout());
    setLayout(new BorderLayout(10, 0));
    

    This will give space between each label/component.

    Then I would add:

    add(Box.createRigidArea(new Dimension(50, 0)));
    //add(Box.createHorizontalGlue());
    

    Do you really need the glue? The above will give fixed spacing between each panel.