How do I prevent component shaking with GridBagLayout?

Viewed 67

I have a GUI with four components laid out next to each other with GridBagLayout. When I resize the window to the right, the leftmost component shakes, when I resize to the left, the rightmost.

Here is an example:

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

public class GBLShakeTest extends JPanel {
    public static void main(String[] args) {
        JFrame frame = new JFrame("GBLShakeTest");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(500, 500));
        frame.add(new GBLShakeTest());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public GBLShakeTest() {
        super(new GridBagLayout());
        add(new JTextArea("component 1"), new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
        add(new JTextArea("component 2"), new GridBagConstraints(1, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
        add(new JTextArea("component 3"), new GridBagConstraints(2, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
        add(new JTextArea("component 4"), new GridBagConstraints(3, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
    }
}

It even happens with the GridBagLayout demo from oracle which uses only JButtons. Resizing to the right is fine, but when you enlarge the window leftwards, the right Button shakes. Does anyone know how to fix this?

1 Answers

This is definitely a bug in GridBagLayout. I can’t find a workaround for it, at least not using GridBagLayout.

What seems to happen is that GridBagLayout changes the first component’s X coordinate from 0 to 1, for some container sizes. If only the first component has a nonzero weightx, the problem doesn’t occur (which makes me suspect that it’s an issue with the inaccuracy of floating-point weightx values, but I haven’t tried digging into the GridBagLayout source to confirm that).

One alternative is to use SpringLayout. SpringLayout is difficult to use, but it can do many of the things GridBagLayout can, with some work. (People who have written code that uses Motif’s XmForm widget will recognize the use of attachments to other components, though SpringLayout does not work in exactly the same manner.)

SpringLayout layout = new SpringLayout();
setLayout(layout);

add(new JTextArea("component 1"));
add(new JTextArea("component 2"));
add(new JTextArea("component 3"));
add(new JTextArea("component 4"));

Component previous = null;
for (Component c : getComponents()) {
    // Attach component to top and bottom of container.
    layout.putConstraint(
        SpringLayout.NORTH, c, 0, SpringLayout.NORTH, this);
    layout.putConstraint(
        SpringLayout.SOUTH, c, 0, SpringLayout.SOUTH, this);

    if (previous != null) {
        // Attach component's left (west) edge to previous component's
        // right(east) edge.
        layout.putConstraint(
            SpringLayout.WEST, c, 0, SpringLayout.EAST, previous);
    }

    previous = c;
}

// Bind this container's right (east) edge to the right (east) edge
// of the rightmost child component.
Component lastComponent = getComponent(getComponentCount() - 1);
layout.putConstraint(
    SpringLayout.EAST, this, 0, SpringLayout.EAST, lastComponent);
Related