Java Graphics2D如何实现点击后移动BufferedImage棋子
棋子移动不生效的核心原因
paintComponent方法硬编码了初始棋子的绘制逻辑,完全没有读取piecePosition里存储的动态棋子位置数据,不管你怎么修改位置数据,重绘时永远只会在初始开局位置画棋子。makeChanges方法没有实现核心的位置更新逻辑,既没有修改piecePosition里的棋子对应位置,也没有调用repaint()触发界面重绘,移动操作的逻辑没有闭环。- 坐标计算存在混用宽高的问题,计算纵向格子坐标时错误使用了宽度作为除数,边界情况会算错格子位置。
- 鼠标按下时错误修改了整个JPanel的背景色,棋盘格子的颜色本身是在
paintComponent里通过填充矩形实现的,修改面板根背景不会产生选中格子高亮的效果。
修复方案
1. 重构棋子绘制逻辑
删除paintComponent里所有硬编码判断y==0、y==1、y==6、y==7绘制棋子的冗余代码,改为遍历piecePosition动态绘制每个格子的棋子,同时补全Graphics2D资源释放逻辑:
@Override protected void paintComponent (Graphics graphic){ super.paintComponent(graphic); Graphics2D g2d = (Graphics2D) graphic.create(); // 开抗锯齿让画面更平滑 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); boolean cellColorToggle = true; int cellSize = getWidth() / 8; g2d.setFont(font.deriveFont(Font.PLAIN, 18)); // 用你加载的自定义字体 for(int y = 0; y < 8; y++){ for(int x = 0; x < 8; x++){ // 画棋盘格子 if(cellColorToggle){ g2d.setColor(Color.decode("#f7e7ce")); } else { g2d.setColor(Color.decode("#B58863")); } g2d.fillRect(x*cellSize, y*cellSize, cellSize, cellSize); // 画坐标标注 if(y == 7){ g2d.setColor(Color.BLACK); g2d.drawString(String.valueOf(x_AxisMap.get(x)), x*cellSize+3, y*cellSize + cellSize - 3); } if(x == 7){ g2d.setColor(Color.BLACK); g2d.drawString(String.valueOf(y_AxisMap.get(y)), x*cellSize + cellSize - 15, y*cellSize + 15); } // 动态画棋子 int pos = positions[y][x]; String pieceInfo = piecePosition.get(pos); if(pieceInfo != null){ String[] split = pieceInfo.split(" "); String pieceColor = split[0]; String pieceType = split[1]; BufferedImage targetPiece = null; switch (pieceType){ case "ROOK": targetPiece = pieceColor.equals("white") ? Piece.ROOK.white() : Piece.ROOK.black(); break; case "KNIGHT": targetPiece = pieceColor.equals("white") ? Piece.KNIGHT.white() : Piece.KNIGHT.black(); break; case "BISHOP": targetPiece = pieceColor.equals("white") ? Piece.BISHOP.white() : Piece.BISHOP.black(); break; case "QUEEN": targetPiece = pieceColor.equals("white") ? Piece.QUEEN.white() : Piece.QUEEN.black(); break; case "KING": targetPiece = pieceColor.equals("white") ? Piece.KING.white() : Piece.KING.black(); break; case "PAWN": targetPiece = pieceColor.equals("white") ? Piece.PAWN.white() : Piece.PAWN.black(); break; } if(targetPiece != null){ g2d.drawImage(targetPiece, x*cellSize, y*cellSize, cellSize, cellSize, this); } } cellColorToggle = !cellColorToggle; } cellColorToggle = !cellColorToggle; } g2d.dispose(); // 释放创建的Graphics2D资源避免内存泄漏 }
2. 补全移动逻辑
修改makeChanges方法,实现棋子位置更新、界面重置和重绘触发:
public void makeChanges(){ // 把之前乱改的面板背景重置 setBackground(Color.decode("#ebe4e4")); if(pressedPosition == releasedPosition) { repaint(); return; } // 获取要移动的棋子 String movePiece = piecePosition.get(pressedPosition); if(movePiece == null) { repaint(); return; } // 这里可以加国际象棋规则判断,比如是否允许走这个位置、是否是己方回合、是否将军之类的 // 更新位置映射:起点清空,终点放置移动的棋子(实现吃子直接覆盖即可) piecePosition.put(pressedPosition, null); piecePosition.put(releasedPosition, movePiece); // 清空临时存储的选中棋子数据 pressedPiece = null; releasedPiece = null; // 触发重绘 repaint(); }
3. 修正坐标计算错误
把mousePressed和mouseReleased里的坐标计算统一用格子边长,避免宽高混用:
// mousePressed里的坐标计算 int cellSize = getWidth()/8; pressedX = e.getX() / cellSize; pressedY = e.getY() / cellSize; pressedPosition = getNumberPosition(pressedX, pressedY); // mouseReleased里的坐标计算 int cellSize = getWidth()/8; releasedX = e.getX() / cellSize; releasedY = e.getY() / cellSize; releasedPosition = getNumberPosition(releasedX, releasedY);
其他优化点
- 加载自定义字体后,需要注册到本地图形环境才能正常显示,在字体加载的try块里加一行:
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font); - 如果要实现选中格子高亮的效果,不要改面板背景,加一个成员变量存储选中的格子坐标,在
paintComponent里对应位置画一个半透明的高亮矩形即可。 - 现在的棋子图片没有指定绘制大小,调用
drawImage的时候传入cellSize作为宽高,棋子会自动适配格子大小,不会出现缩放错位。
内容的提问来源于stack exchange,提问作者ToyotaCorolla
相关产品推荐
相关产品推荐

