How can I force 4 cells per row with equal width in Miglayout when only one cell is used?

Viewed 865

I got a panel with a dynamic width. Components added to the panel should be arranged left-to-right up to 4 components (cells) per row, where each component/cell fill 25% of the panels width. If there is only one component, it should fill 25% of the parents width - the remaining 75% (3 cells) of the space should be empty.

I got it to work using a hack, but I'm not happy about that implementation - using cells and creating an "empty" panel for each cell that is not used. See the code snippet below:

MigLayout migLayout = new MigLayout("fillx, insets 0, wrap 4", "", "");
JPanel panel = new JPanel(migLayout);
JLabel label1 = new JLabel("1");
JLabel label2 = new JLabel("2");
JPanel filler1 = new JPanel();
JPanel filler2 = new JPanel();
panel.add(label1, "cell 0 0);
panel.add(label2, "cell 1 0);
panel.add(filler1, "cell 2 0);
panel.add(filler2 , "cell 3 0);

This gives something that can be illustrated like below, where the outer bracket is the outer panel, and the two inner brackets are the labels:

[  [ 1 ]  [ 2 ]                ]

Instead of using fillers I was hoping that it could be set with constraints, something like this:

MigLayout migLayout = new MigLayout("always 4 cells, fillx", "[fill][fill][fill][fill]", "");
JPanel panel = new JPanel(migLayout);
JLabel label1 = new JLabel();
JLabel label2 = new JLabel();
panel.add(label1);
panel.add(label2);

I have tried various layout constraints and add component parameters, but they are typically formatted like this:

[  [ 1 ]         [ 2 ]         ]
3 Answers

I would go with a GridLayout. GridLayout will separate your panel into cells according the given rows/cols (check its constructor). Then each component will fit 100% (width and height) to the cell.

However: If you set the grid layout to 1 row and 4 columns (your case) and you add only 1 component, the layout will be adjusted like: 1row and 1 column, because it won't let empty space.

Trick/Solution: Add an empty component exactly the same way we add gap to a BoxLayout.

private static Component createSpace() {
    return Box.createRigidArea(new Dimension(1, 1));
}

Disadvantage: If you want to add a component to the panel after it is being shown, you must remove the spaces, so you would have either to store all empty components into a structure or do the following (I always prefer it):

gridLayoutPanel.removeAll();
gridLayoutPanel.add(component1);
gridLayoutPanel.add(newComponent); //This was space before
gridLayoutPanel.add(createSpace());
gridLayoutPanel.add(createSpace());
gridLayoutPanel.repaint();
gridLayoutPanel.revalidate();

An SSCCE would be (ignore the height of the components):

import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;

import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class GridLayoutTest extends JFrame {
    public GridLayoutTest() {
        getContentPane().setLayout(new GridLayout(1, 4));
        JButton component1 = new JButton("Component1");
        JButton component2 = new JButton("Component2");
        JButton component3 = new JButton("Component3");
        add(component1); //25% of the width
        add(component2); //25% of the width
        add(component3); //25% of the width
        add(createSpace()); //25% of the width
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(500, 200);
        setLocationRelativeTo(null);
    }

    private static Component createSpace() {
        return Box.createRigidArea(new Dimension(1, 1));
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new GridLayoutTest().setVisible(true));
    }
}

You can use % in the column constraint, and remember to add the '!' so that each column has a fix width.

MigLayout migLayout = new MigLayout("wrap 4, fill", "[25%!,fill][25%!,fill][25%!,fill][25%!,fill]");
JPanel panel = new JPanel(migLayout);
JLabel label1 = new JLabel();
JLabel label2 = new JLabel();
panel.add(label1, "grow"); // add grow here if you want to fill up the 25%
panel.add(label2, "grow");

also check out http://www.miglayout.com/whitepaper.html, all the miglayout tricks are in it

Probably a bit late, but I think this the solution:

public class Test extends JFrame {

    public Test() {
        // Col Constraint
        String ccol="[fill, 25%:25%:25%]";
        // Cell Constraint
        String ccell="";
        MigLayout migLayout = new MigLayout("debug, fillx, insets 5, wrap 4", ccol+ccol+ccol+ccol, "");
        Container panel = getContentPane();
        panel.setLayout(migLayout);
        MyComponent label;

        // row 1
        label = new MyComponent("1.1");
        panel.add(label, ccell + "cell 0 0");
        label = new MyComponent("1.2");
        panel.add(label, ccell + "cell 1 0");

        // row 2
        label = new MyComponent("2.1");
        panel.add(label, ccell + "cell 0 1");


        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(500, 200);
        setLocationRelativeTo(null);
    }



    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Test().setVisible(true));
    }

    private static class MyComponent extends JLabel {

        public MyComponent(String text) {
            super(text);
            setOpaque(true);
            setBackground(Color.YELLOW);
        }

    }

}
Related