javaswinglayout-managergridbaglayout

GridBagLayout gives extra margin to column


I've been working with Swing for quite some time and I finally found the self confidence to learn GridBadLayout.

I'm still learning it, and in this case I can't understand why the following code is not responding as expected: in particular I can't understand why the layout is displaying the columns this way.

By running the snippet, you will se that the panels representing the Italian flag are not positioned correctly: the last column(red part of the flag) is detached from the rest of the flag(white part of the flag). So, what am I doing wrong and what can I fix in order to correctly represent the flag?

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class test extends JPanel {

    private GridBagConstraints gbc;
    private final int CELL_WIDTH = 90;
    private final int CELL_HEIGHT = 110;

    public test() {
        setLayout(new GridBagLayout());

        putBanner(0);

        putFlagRow(1);
        putFlagRow(2);
        putFlagRow(3);
        putFlagRow(4);
    }

    public JPanel getUserPanel(Color c) {
        JPanel ol = new JPanel();
        ol.setPreferredSize(new Dimension(CELL_WIDTH * 10, CELL_HEIGHT));
        ol.setBackground(c);
        return ol;
    }

    public JPanel gettestPanel() {
        JPanel ol = new JPanel();
        ol.setPreferredSize(new Dimension(CELL_WIDTH, CELL_HEIGHT));
        ol.setBackground(Color.white);
        return ol;
    }

    private void putFlagRow(int gridy) {
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = gridy;
        JPanel p1=gettestPanel();
        p1.setBackground(Color.green);
        add(p1, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 2;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 3;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 4;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 5;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 6;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 7;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 8;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 9;
        gbc.gridy = gridy;
        JPanel p=gettestPanel();
        p.setBackground(Color.red);
        add(p, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 10;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);
    }

    private void putBanner(int gridy) {
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = gridy;
        gbc.gridwidth = 1;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = gridy;
        gbc.gridwidth = 9;
        add(getUserPanel(Color.black), gbc);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("GridBagLayoutTest");
        frame.add(new test());
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

Besides understanding this particular issue and its cause, I wish to understand this, which comes directly from GridBagLayout Oracle docs:

weightx, weighty Specifying weights is an art that can have a significant impact on the appearance of the components a GridBagLayout controls. Weights are used to determine how to distribute space among columns (weightx) and among rows (weighty); this is important for specifying resizing behavior. Unless you specify at least one non-zero value for weightx or weighty, all the components clump together in the center of their container. This is because when the weight is 0.0 (the default), the GridBagLayout puts any extra space between its grid of cells and the edges of the container. Generally weights are specified with 0.0 and 1.0 as the extremes: the numbers in between are used as necessary. Larger numbers indicate that the component's row or column should get more space. For each column, the weight is related to the highest weightx specified for a component within that column, with each multicolumn component's weight being split somehow between the columns the component is in. Similarly, each row's weight is related to the highest weighty specified for a component within that row. Extra space tends to go toward the rightmost column and bottom row.

Expressions like

is an art

and

the numbers in between are used as necessary

really gives me the impression that not even the doc writer thought it was possible to teach the usage of such constraint. And that is no good


Solution

  • the last column(red part of the flag) is detached from the rest of the flag(white part of the flag).

    One thing to do when debugging a GridBagLayout is to add a Border to the components so you can see how each cell is sized. I added the following to your getTestPanel() method:

    ol.setBorder( new LineBorder(Color.BLUE) );
    

    You can see the panel is sized properly but there is extra space around the panel.

    Then you look at your user panel:

    ol.setPreferredSize(new Dimension(CELL_WIDTH * 10, CELL_HEIGHT));
    

    And you will see the size is set to 10 cells even though you only set the gridwidth to 9 cells. So now the 9 cells must fit into the space of the user panel and it looks like the extra space is given to the last column of the 9 columns.

    The code should be:

    //ol.setPreferredSize(new Dimension(CELL_WIDTH * 10, CELL_HEIGHT));
    ol.setPreferredSize(new Dimension(CELL_WIDTH * 9, CELL_HEIGHT));
    

    Another option would be to NOT give a preferred size to the user panel, but then use the "fill" constraint so the user panel will now take up the same space as the nine columns.

    weightx, weighty Specifying weights is an art...

    This is not related to your problem. To see this effect, drag the frame larger. Now all the entire panel will be positioned in the center of the frame since none of your component use the weightx/weighty constraints. This means none of the components will grow to fill the empty space if the frame.

    If one of the components has a weightx constraint of 1.0 then that column will expand and fill the extra space as the frame is resized.