使用来自 Jlabel 的自定义子类时 GridBagLayout 堆叠标签

GridBagLayout stacks labels when using custom subclass from Jlabel

我正在用 Swing 编写 GUI。我正在使用 GridBagLayout 在网格中显示多个 JLabels(基本上就像棋盘)。一旦我使用来自 JLabel 而不是 JLabel 的自制标签 class,GridBagLayout 就会将每个标签堆叠在 JPanel 的左上角.

要么我的 subclass TileLabel 不正确,要么我没有正确使用布局和约束。我认为是最后一个,因为我看不出在这么小的 subclass.

中会有什么问题

这是使用 JLabel 的样子(L 代表标签):

(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

这是使用 TileLabel 时的样子(S 代表堆叠的所有标签):

(MenuBar)
S 

这是我来自 JLabel 的简单子class:

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

这是 GUI class。我标记了我使用自定义标签导致布局问题的三行。

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

问题出在哪里?

您不小心覆盖了 JComponent#getX() and JComponent#getY()。此方法返回的值 not 与布局可能在内部设置的值(通过调用 setBounds 左右)一致。这弄乱了布局。

(诚然,我并没有真正检查是否是这个原因,但很可能是,而且这是一个普遍的问题!)

您的代码中有很多问题需要修复,但您的问题源于 3 个问题:

  1. 您在自定义标签中的方法定义:

    public class TileLabel extends JLabel {
    
        // @Override !!!!
        public int getX() {
            return x;
        }
    
        // @Override !!!!
        public int getY() {
            return y;
        }
    }
    

    您正在覆盖 JComponentgetX()getY(),它们负责返回坐标。这完全弄乱了布局。

    小心你的 paint 方法,一个同名的方法存在于 superclass 中,尽管在这种情况下你得救了,因为参数不同。

  2. 你的循环有错别字:gui.paint(tile, x, x) 应该是 gui.paint(tile, x, y).

  3. 您调用方法的顺序错误。首先,您创建框架并显示它,然后通过向其添加带有标签的面板来更改其内容,然后更改标签中的文本。你应该反过来做。

我的建议:

  • 让您的 paint 方法成为您的 TileLabel class 的成员。这更有意义。
  • 在创建标签时设置图标,除非它们是未知的。如果不能,您可能需要重新计算 space 要求。
  • 永远不要让您的布局依赖于屏幕的大小或分辨率。它构成了一个脆弱的 GUI(如评论中所述)。使用 pack() 作为框架来计算正确的尺寸。