3

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 ]         ]
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Marcus Persson
  • 373
  • 4
  • 20

3 Answers3

1

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));
    }
}
George Z.
  • 6,643
  • 4
  • 27
  • 47
  • Good point, but not the answer to my question. However, I suppose this is the solution I will go for if MigLayout isn't magical enough. Thanks! – Marcus Persson Jan 15 '19 at 10:36
  • @Marcus Persson Could you explain what is missing and why this is not covering you? I have read the original post few times but i am not able to find what did i do wrong. You are allowed to use only mig layout? – George Z. Jan 15 '19 at 10:50
  • I would really want to not use filler panels, etc, because in the end I got a dynamic panel that you can add/remove components from. Managing this with filler panels is annoying. Also, it's easy to manage gap size, indents, etc., with MigLayout. So, I was really hoping that it was possible to do with MigLayout without fillers. – Marcus Persson Jan 15 '19 at 11:57
  • @MarcusPersson Using fillers is not a bad practice. – George Z. Jan 15 '19 at 12:00
  • If you don't have to, it is. :) As said, I want to be able to add/remove components dynamically. With fillers, if I remove a component I have to add another filler, or remove a filler when I add a new component. If there is an alternative, I'd prefer that, which is why I said I would go with your solution if I can't get MigLayout to work with me (or some other solution that doesn't require fillers). – Marcus Persson Jan 15 '19 at 12:05
0

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

Dalancer
  • 1
  • 1
0

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);
        }

    }

}
lvr123
  • 524
  • 6
  • 24