You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Swing JButton点击后切换两种颜色的问题解决(井字棋场景)

基于颜色的井字棋回合颜色无法切换问题修复

核心问题原因

你的代码里,turns变量被声明在actionPerformed方法内部,每次按钮点击时都会重新初始化turns = 0,之后执行turns++得到1,永远满足turns % 2 != 0的条件,所以每次点击都只会设置蓝色,完全无法实现回合切换。

另外还有几个次要问题需要修正:

  • 每个按钮都创建了独立的ButtonListener实例,但回合计数器应该是全局共享的
  • 没有限制按钮重复点击,玩家可以多次点击同一个格子修改颜色
  • 冗长的if-else判断可以大幅简化

修复后的完整代码

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Board extends JPanel {
    boolean gameRunning = true;
    private final JButton[][] grid = new JButton[3][3];
    private int turns = 0; // 全局回合计数器,移到Board类作为成员变量
    JFrame board;
    JPanel panel;

    public Board() {
        initBoard();
    }

    private void makePanel() {
        panel = new JPanel();
        panel.setBackground(Color.lightGray);
        panel.setLayout(new GridLayout(3, 3));
        setBoard();
        board.add(panel);
    }

    private void initBoard() {
        int BOARD_WIDTH = 800;
        int BOARD_HEIGHT = 600;

        board = new JFrame();
        board.setTitle("JTicTacToe");
        board.setSize(BOARD_WIDTH, BOARD_HEIGHT);
        board.setResizable(false);
        board.setLocationRelativeTo(null);
        board.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        makePanel();
        board.setVisible(true);
    }

    private void setBoard() {
        Color defaultColor = new Color(238,238,238);
        // 遍历生成3x3网格按钮
        for (int row = 0; row < grid.length; row++) {
            for (int col = 0; col < grid[row].length; col++) {
                grid[row][col] = new JButton();
                grid[row][col].setOpaque(true);
                grid[row][col].setBorderPainted(false);
                grid[row][col].setBackground(defaultColor);
                grid[row][col].addActionListener(new ButtonListener());
                panel.add(grid[row][col]);
            }
        }
        validate();
    }

    protected class ButtonListener implements ActionListener {
        Color defaultColor = new Color(238,238,238);

        protected void check(int moves) {
            // 检查行
            for (int c = 0; c < 3; c++) {
                if (!defaultColor.equals(grid[c][0].getBackground())
                        && grid[c][0].getBackground().equals(grid[c][1].getBackground())
                        && grid[c][0].getBackground().equals(grid[c][2].getBackground())) {
                    gameRunning = false;
                    JOptionPane.showMessageDialog(null, "Game Over!");
                }
            }
            // 检查列
            for (int c = 0; c < 3; c++) {
                if (!defaultColor.equals(grid[0][c].getBackground())
                        && grid[0][c].getBackground().equals(grid[1][c].getBackground())
                        && grid[0][c].getBackground().equals(grid[2][c].getBackground())) {
                    gameRunning = false;
                    JOptionPane.showMessageDialog(null, "Game Over!");
                }
            }

            // 检查对角线
            if (!defaultColor.equals(grid[0][0].getBackground())
                    && grid[0][0].getBackground().equals(grid[1][1].getBackground())
                    && grid[0][0].getBackground().equals(grid[2][2].getBackground())) {
                gameRunning = false;
                JOptionPane.showMessageDialog(null, "Game Over!");
            }

            if (!defaultColor.equals(grid[0][2].getBackground())
                    && grid[0][2].getBackground().equals(grid[1][1].getBackground())
                    && grid[0][2].getBackground().equals(grid[2][0].getBackground())) {
                gameRunning = false;
                JOptionPane.showMessageDialog(null, "Game Over!");
            }

            // 检查平局(仅在未分出胜负时触发)
            if(moves == 9 && gameRunning) {
                gameRunning = false;
                JOptionPane.showMessageDialog(null, "Draw!");
            }
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (!gameRunning) return; // 游戏结束后不再响应点击

            JButton clickedButton = (JButton) e.getSource();
            // 仅处理未被点击过的按钮
            if (defaultColor.equals(clickedButton.getBackground())) {
                turns++;
                // 根据回合数切换颜色
                if (turns % 2 == 0) {
                    clickedButton.setBackground(Color.orange);
                } else {
                    clickedButton.setBackground(Color.blue);
                }
                check(turns);
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(Board::new);
    }
}

关键修改点说明

  1. 全局回合计数器:把turns从方法内部移到Board类作为成员变量,确保所有按钮点击共享同一个回合计数
  2. 简化点击判断:直接通过e.getSource()获取点击的按钮,替代冗长的9个if-else分支
  3. 防止重复点击:添加判断,只有按钮背景为默认颜色时才处理点击
  4. 游戏结束锁定:游戏结束后(gameRunning为false)不再响应任何按钮点击
  5. 颜色比较优化:用equals()替代==比较Color对象,避免引用比较的潜在问题
  6. 平局判断优化:添加gameRunning判断,避免在已经分出胜负后还弹出平局提示

内容的提问来源于stack exchange,提问作者Cr3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 11:45:57