jpanels 网格(用于连接四个游戏)- paintComponent 仅适用于左上面板

Grid of jpanels (for connect four game) - paintComponent only works for top left panel

我正在尝试通过使用 JPanel 的网格来制作一个简单的连接四个 GUI,当按下它们下面的按钮并且它下面的面板已满时,每个 JPanel 都会绘制一个彩色磁盘。在添加游戏规则之前,我只是想确保按钮和显示正常工作。但它不起作用 - 只有左上角的面板显示一个磁盘(按下按钮 1 6 次后)。这是我的代码:

public class ConnectFourFrame extends JFrame {

private final JPanel gamePanelsPanel; // panel to hold the game panels
private final GamePanel[][] gamePanels; // a 2D array to hold the grid of panels to display the game disks
private final JPanel buttonsPanel; // panel to hold the buttons panels
private final JPanel gameButtonsPanel; // panel to hold the game buttons to add disk to a column
private final JButton[] gameButtons; // an array to hold the game buttons
private final JPanel clearButtonPanel; // panel to hold the clear button
private final JButton clearButton; // button to clear the game grid from disks

private enum Turn {RED_PLAYER, BLUE_PLAYER}; // keeps track of which players turn it is 
private Turn turn;


// no argument constructor
public ConnectFourFrame() {

    super("Connect Four");
    this.setLayout(new BorderLayout());

    //add panels to hold the game panel and the buttons
    gamePanelsPanel = new JPanel();
    add(gamePanelsPanel, BorderLayout.CENTER);  
    buttonsPanel = new JPanel();
    buttonsPanel.setLayout(new BorderLayout());
    add(buttonsPanel, BorderLayout.SOUTH);

    //set up game panels
    gamePanelsPanel.setLayout(new GridLayout(6,7,3,3));
    gamePanelsPanel.setBackground(Color.BLACK);
    gamePanels = new GamePanel[6][7];
    for (int i = 0; i < 6; i++) {
        for (int j = 0; j < 7; j++) {
            gamePanels[i][j] = new GamePanel(false, Color.WHITE);
            gamePanelsPanel.add(gamePanels[i][j]);
        }
    }


    //set up game and clear buttons
    gameButtonsPanel = new JPanel();
    gameButtonsPanel.setLayout(new GridLayout(1,7));
    clearButtonPanel = new JPanel();

    gameButtons = new JButton[7];
    for (int i = 0; i < 7; i++) {
        gameButtons[i] = new JButton("" + (i+1));
        gameButtonsPanel.add(gameButtons[i]);
    }

    clearButton = new JButton("CLEAR");
    clearButtonPanel.add(clearButton);

    buttonsPanel.add(gameButtonsPanel, BorderLayout.NORTH);
    buttonsPanel.add(clearButtonPanel, BorderLayout.SOUTH);
    add(buttonsPanel, BorderLayout.SOUTH);

    // register event handlers
    ClearButtonHandler clearButtonHandler = new ClearButtonHandler();
    clearButton.addActionListener(clearButtonHandler);
    GameButtonHandler gameButtonHandler = new GameButtonHandler();
    for (int i = 0; i < 7; i++) {
        gameButtons[i].addActionListener(gameButtonHandler);
    }

    turn = Turn.RED_PLAYER; //set first turn to player1

}

// inner class for game button event handling
private class GameButtonHandler implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        // get the number of the pressed button
        int pressedButtonNum = Integer.parseInt(((JButton) e.getSource()).getActionCommand());
        // display disk in top empty panel of the column 
        for (int i = 5; i >= 0; i--) {
            if (!gamePanels[i][pressedButtonNum - 1].isFull()) {
                if (turn == Turn.RED_PLAYER) {
                    gamePanels[i][pressedButtonNum - 1].setDiskColor(Color.RED);
                    turn = Turn.BLUE_PLAYER;
                }
                else {
                    gamePanels[i][pressedButtonNum - 1].setDiskColor(Color.BLUE);
                    turn = Turn.RED_PLAYER;
                }
                gamePanels[i][pressedButtonNum - 1].setFull(true);
                gamePanels[i][pressedButtonNum - 1].repaint();
                return;
            }
        }
            // if column is full display message to try again
            JOptionPane.showMessageDialog(gamePanelsPanel, "Column " + pressedButtonNum + " is full. Try again.");
    }

}

public class GamePanel extends JPanel{

private boolean isFull; // true if the panel has a disk in it. default is empty (false).
private Color diskColor; //color of disks. default is white (same as background)


public GamePanel(boolean isFull, Color diskColor) {
    super();
    this.isFull = isFull;
    this.diskColor = diskColor;
}

public Color getDiskColor() {
    return diskColor;
}

public void setDiskColor(Color diskColor) {
    this.diskColor = diskColor;
}

public boolean isFull() {
    return isFull;
}

public void setFull(boolean isFull) {
    this.isFull = isFull;
}


@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    this.setBackground(Color.WHITE);

    g.setColor(diskColor);
    g.fillOval(this.getX() + this.getWidth()/4 , this.getY() + this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
}

}

问题就在这里...

g.fillOval(this.getX() + this.getWidth()/4 , this.getY() + this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);

传递给你的paintComponent方法的Graphics上下文已经被组件x/y位置翻译了,这意味着top/left角组件的总是 0x0

g.fillOval(this.getWidth()/4 , this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);

可能会更好

此外,在 paintComponent 中调用 this.setBackground(Color.WHITE); 是不可取的,因为它会设置一个新的绘制周期将被安排的情况,一遍又一遍。不要在绘画方法

中改变UI的状态