Search code examples
javaswingawtjlabelgridbaglayout

GridBagLayout stacks labels when using custom subclass from Jlabel


I am writing a GUI with Swing. I'm using a GridBagLayout to display multiple JLabels in a grid (basically like a chess board). As soon as I use a self made label class derived from JLabel instead of JLabel, the GridBagLayout stacks every label on the top left corner of the JPanel.

Either my subclass TileLabel is incorrect or I don't use the layout and constraints the right way. I think the last one because I can't see what would be a problem in such a minimal subclass.

This is how it looks using JLabel (L represents a label):

(MenuBar)
L L L L L L L L L
L L L L L L L L L
L L L L L L L L L

This is how it looks using TileLabel (S represents all the labels stacked):

(MenuBar)
S 

This is my simple subclass from JLabel:

import javax.swing.JLabel;

public class TileLabel extends JLabel {
    private static final long serialVersionUID = 6718776819945522562L;
    private int x;
    private int y;

    public TileLabel(int x, int y) {
        super();
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }
}

And this is the GUI class. I Marked the three lines where I used my custom label which lead to the layout problem.

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class MainGUI extends JPanel {
    private static final long serialVersionUID = -8750891542665009043L;
    private JFrame frame;
    private MainMenuBar menuBar;
    private TileLabel[][] labelGrid; // <-- LINE 1
    private GridBagConstraints constraints;
    private int gridWidth;
    private int gridHeight;

     // Basic constructor.
    public MainGUI(int frameWidth, int frameHeight) {
        super(new GridBagLayout());
        constraints = new GridBagConstraints();
        buildFrame(frameWidth, frameHeight);
        buildLabelGrid(frameWidth, frameHeight);
    }

    // Builds the frame.
    private void buildFrame(int frameWidth, int frameHeight) {
        menuBar = new MainMenuBar();
        frame = new JFrame("Carcasonne");
        frame.getContentPane().add(this);
        frame.setJMenuBar(menuBar);
        frame.setResizable(false);
        frame.setVisible(true);
        frame.setSize(frameWidth, frameHeight);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBackground(new Color(165, 200, 245));
    }

    // Creates the grid of labels.
    private void buildLabelGrid(int frameWidth, int frameHeight) {
        gridWidth = frameWidth / 100;
        gridHeight = frameHeight / 100;
        labelGrid = new TileLabel[gridWidth][gridHeight]; // <-- LINE 2
        for (int x = 0; x < gridWidth; x++) {
            for (int y = 0; y < gridHeight; y++) {
                labelGrid[x][y] = new TileLabel(x, y); // <-- LINE 3
                constraints.gridx = x;
                constraints.gridy = y;
                add(labelGrid[x][y], constraints); // add label with constraints
            }
        }
    }

    // sets the icon of a specific label
    public void paint(Tile tile, int x, int y) {
        if (x >= 0 && x < gridWidth && y >= 0 && y < gridHeight) {
            labelGrid[x][y].setIcon(tile.getImage());
        } else {
            throw new IllegalArgumentException("Invalid label grid position (" + x + ", " + y + ")");
        }
    }

    // Just to test this GUI:
    public static void main(String[] args) {
        MainGUI gui = new MainGUI(1280, 768);
        Tile tile = TileFactory.createTile(TileType.Road);
        for (int x = 0; x < 12; x++) {
            for (int y = 0; y < 7; y++) {
                gui.paint(tile, x, x);
            }
        }
    }
}

Where is the problem?


Solution

  • Override

    You have accidentally overridden JComponent#getX() and JComponent#getY(). The values returned by this method are not consistent with the values that the layout may set internally (via calls to setBounds or so). This messes up the layout.

    (Admittedly, I did not really check whether this is the reason, but it likely is, and it is a problem in general!)