Search code examples
javaswinggridbaglayout

Odd behavior with GridBagLayout, grid positions partially ignored


I have a very odd problem with GridBagLayout, where the layout seemingly "adjusts" the grid positions to make components align in neat columns - exactly what I do not want. I want my components aligned in brick-wall fashion, but I get a checkerboard layout.

This example reproduces the case:

public class GBLayout extends JPanel {

    public GBLayout() {
        setLayout(new GridBagLayout());
        for (int y=0; y<10; ++y) {
            int offset = y & 1;
            if (offset != 0) {
                GridBagConstraints c = new GridBagConstraints(
                        0, y, // x, y
                        1, 1, // w, h
                        0D, 0D,
                        GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL,
                        new Insets(2, 2, 2, 2),
                        0, 0);
                add(new JLabel("!"), c);
            }
            for (int x=0; x<10; ++x) {
                int bx = x * 2 + offset;
                int by = y;
                JButton b = new JButton("(" + bx + ", " + by + ")");
                GridBagConstraints c = new GridBagConstraints(
                        bx, by, // x, y
                        2, 1, // w, h
                        1D, 0D,
                        GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL,
                        new Insets(2, 2, 2, 2),
                        0, 0);
                add(b, c);
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                GBLayout panel = new GBLayout();
                JFrame frame = new JFrame("Test");
                frame.setLayout(new BorderLayout());
                frame.add(panel, BorderLayout.CENTER);
                frame.pack();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }
}

Thats how it currently looks:

Layout as obseved from the example

Expected Layout is a brick wall (approximately, I edited this manually):

Approximate expected layout

Thats only obeyed in the leftmost column, after that GridBagLayout seems to have its own idea about where to position my components. Whats causing this and how can I get around it?


Solution

  • This will give you desired effect, on my computer it looks like this:

    enter image description here

    import java.awt.BorderLayout;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    import java.awt.Insets;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class GBLayout extends JPanel {
    
        public GBLayout() {
            GridBagLayout gridBagLayout = new GridBagLayout();
            gridBagLayout.columnWeights = new double[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
            setLayout(gridBagLayout);
            for (int y=0; y<10; ++y) {
                int offset = y & 1;
                if (offset != 0) {
                    GridBagConstraints c = new GridBagConstraints(
                            0, y, // x, y
                            1, 1, // w, h
                            0D, 0D,
                            GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL,
                            new Insets(2, 2, 2, 2),
                            0, 0);
                    add(new JLabel("!"), c);
                }
                for (int x=0; x<10; ++x) {
                    int bx = x * 2 + offset;
                    int by = y;
                    JButton b = new JButton("(" + bx + ", " + by + ")");
                    GridBagConstraints c = new GridBagConstraints(
                            bx, by, // x, y
                            2, 1, // w, h
                            1D, 1D,
                            GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL,
                            new Insets(2, 2, 2, 2),
                            0, 0);
                    add(b, c);
                }
            }
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    GBLayout panel = new GBLayout();
                    JFrame frame = new JFrame("Test");
                    frame.setLayout(new BorderLayout());
                    frame.add(panel, BorderLayout.CENTER);
                    frame.pack();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setVisible(true);
                }
            });
        }
    }
    

    The trick is to set the table columnWeights to 1, because without that, colums can have different sizes, some columns can be streched to button width, or smashed to 0.