Java Swing象棋棋盘拖拽与棋子移动问题(repaint()无效)
Java Swing象棋拖拽移动失效问题解决
问题描述
我用Java Swing开发象棋GUI,基于JPanel实现了Board类负责绘制棋盘和棋子,同时通过MouseListener和MouseMotionListener实现鼠标拖拽移动棋子的逻辑。现在遇到的问题是:控制台显示棋子坐标已经更新,但界面上看不到棋子移动,repaint()调用后画面没有变化。
问题代码
Board类代码
public class Board extends JPanel implements MouseMotionListener, MouseListener { private final PieceRender pieceRender = new PieceRender(); private final int cellSize = 600 / 8; private final int initialX = 45; private final int initialY = 55; private Point movingPiece; private int pieceLocationIndex; public Board() { addMouseListener(this); addMouseMotionListener(this); } @Override protected void paintComponent(Graphics g){ super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; drawBoard(g2); drawLabel(g2); pieceRender.pieceByDefault(g2,initialX,initialY,cellSize); } private void drawBoard(Graphics2D g2) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { drawSquare(g2,2 * j, 2 * i, true); drawSquare(g2,1 + 2 * j, 1 + 2 * i, true); drawSquare(g2,1 + 2 * j, 2 * i, false); drawSquare(g2,2 * j, 1+ 2 * i, false); } } } private void drawSquare(Graphics2D g, int x, int y, boolean color){ g.setColor(color ? Color.WHITE : Color.darkGray); g.fillRect(initialX + x * cellSize, initialY + y * cellSize, cellSize, cellSize); } private void drawLabel(Graphics2D g2){ char[] litere = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'}; char[] cifre = {'1', '2', '3', '4', '5', '6', '7', '8'}; Font font = new Font("default", Font.BOLD, 20); g2.setFont(font); var pozitionX = initialX; var pozitionY = initialY; for(int i = 0; i < litere.length; i++){ g2.drawChars(litere, i, 1, pozitionX + 30, pozitionY - 10); pozitionX += 75; } for(int i = 0; i < cifre.length; i++){ g2.drawChars(cifre, i, 1, pozitionX - 625, pozitionY + 45); pozitionY += 75; } } @Override public void mouseClicked(MouseEvent e) {} @Override public void mousePressed(MouseEvent e) { var actualX = (e.getPoint().x - initialX) / cellSize; var actualY = (e.getPoint().y - initialY) / cellSize; for (int i = 0; i < pieceRender.piecesBox.size(); i++) { if (pieceRender.piecesBox.get(i).getCoordinates().getPos_X() == actualX && pieceRender.piecesBox.get(i).getCoordinates().getPos_Y() == actualY){ pieceLocationIndex = i; } } movingPiece = e.getPoint(); repaint(); } @Override public void mouseReleased(MouseEvent e) { var actualX = (e.getPoint().x - initialX) / cellSize; var actualY = (e.getPoint().y - initialY) / cellSize; pieceRender.piecesBox.get(pieceLocationIndex).getCoordinates().setPos_X(actualX); pieceRender.piecesBox.get(pieceLocationIndex).getCoordinates().setPos_Y(actualY); movingPiece = null; repaint(); } @Override public void mouseDragged(MouseEvent e) { movingPiece = e.getPoint(); repaint(); } @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} @Override public void mouseMoved(MouseEvent e) {} public static void main(String[] args) { Thread t1 = new Thread(() -> { Board board = new Board(); board.addMouseListener(board); board.addMouseMotionListener(board); JFrame frame = new JFrame("Chess Game"); frame.add(board); frame.setSize(800, 800); frame.setVisible(true); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); }); t1.start(); } }
PieceRender类绘制代码
public void pieceByDefault (Graphics2D g2, int initialX, int initialY, int cellSize) { PieceFactory pieceFactory = new PieceFactory(); char[][] defaultPositions = { {'R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R'}, {'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'}, {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, {'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'}, {'r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'} }; for (int y = 0; y < 8; y++) { for (int x = 0; x < 8; x++) { char pieceType = defaultPositions[y][x]; if (pieceType != ' ') { ChessPiece piece = pieceFactory.create(pieceType, new Coordinates(x, y)); piecesBox.add(piece); drawPiece(g2, piece.getIndex(), piece .getCoordinates().getPos_X(), piece.getCoordinates() .getPos_Y(),initialX, initialY, cellSize); } } } } public void drawPiece(Graphics2D g2, int index, int initialX, int initialY,int primeX, int primeY, int cellSize){ drawImage(g2, index, initialX, initialY,primeX, primeY, cellSize); }
问题根源及修复方案
1. 核心问题:每次重绘都重新初始化棋子
pieceByDefault方法在paintComponent中被调用,意味着每次重绘都会重新创建所有棋子并添加到piecesBox,直接覆盖了之前修改的坐标,同时导致棋子列表无限膨胀。
修复:
将棋子初始化逻辑从绘制方法中分离,只在Board构造时执行一次:
// Board类构造方法修改 public Board() { addMouseListener(this); addMouseMotionListener(this); // 初始化棋子,仅执行一次 pieceRender.initPieces(); }
2. 绘制逻辑错误:未基于实时坐标绘制
原pieceByDefault是基于默认位置绘制,而非遍历现有棋子的实时坐标。需要改为根据piecesBox中棋子的当前状态绘制。
修复:
修改PieceRender类,拆分初始化和绘制逻辑:
public class PieceRender { public List<ChessPiece> piecesBox = new ArrayList<>(); // 初始化棋子,仅调用一次 public void initPieces() { PieceFactory pieceFactory = new PieceFactory(); char[][] defaultPositions = { {'R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R'}, {'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'}, {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, {'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'}, {'r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'} }; piecesBox.clear(); for (int y = 0; y < 8; y++) { for (int x = 0; x < 8; x++) { char pieceType = defaultPositions[y][x]; if (pieceType != ' ') { ChessPiece piece = pieceFactory.create(pieceType, new Coordinates(x, y)); piecesBox.add(piece); } } } } // 根据实时坐标绘制所有棋子 public void drawAllPieces(Graphics2D g2, int initialX, int initialY, int cellSize) { for (ChessPiece piece : piecesBox) { Coordinates coord = piece.getCoordinates(); int drawX = initialX + coord.getPos_X() * cellSize; int drawY = initialY + coord.getPos_Y() * cellSize; drawPiece(g2, piece.getIndex(), drawX, drawY, initialX, initialY, cellSize); } } // 原有drawPiece、drawImage方法保持不变 }
3. 拖拽时的实时绘制处理
拖拽过程中,需要单独绘制跟随鼠标的棋子,避免和其他棋子的绘制逻辑冲突。
修复:
修改Board类的paintComponent方法:
@Override protected void paintComponent(Graphics g){ super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; drawBoard(g2); drawLabel(g2); // 绘制所有非拖拽状态的棋子 pieceRender.drawAllPieces(g2, initialX, initialY, cellSize); // 绘制正在拖拽的棋子 if (movingPiece != null) { ChessPiece moving = pieceRender.piecesBox.get(pieceLocationIndex); // 让棋子中心跟随鼠标 int drawX = movingPiece.x - cellSize / 2; int drawY = movingPiece.y - cellSize / 2; pieceRender.drawPiece(g2, moving.getIndex(), drawX, drawY, initialX, initialY, cellSize); } }
4. 其他冗余问题修复
- 移除main方法中重复注册的鼠标监听器(Board构造中已注册)
- 使用
SwingUtilities.invokeLater代替自定义线程,保证Swing组件在事件调度线程(EDT)中创建:
public static void main(String[] args) { SwingUtilities.invokeLater(() -> { Board board = new Board(); JFrame frame = new JFrame("Chess Game"); frame.add(board); frame.setSize(800, 800); frame.setVisible(true); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); }); }
内容的提问来源于stack exchange,提问作者wallhack
相关产品推荐
相关产品推荐

