如何在JFrame中添加JPanel且不清除已绘制的图形?
解决Java Swing国际象棋初始棋子不可见及拖动时棋盘消失问题
问题根源
初始棋子不可见
- 直接在
JFrame的paint方法中绘制棋盘,且未调用super.paint(g),导致添加的PieceImage子组件无法被渲染。 PieceImage作为JPanel,默认未设置合适尺寸,且未开启透明模式,即使被绘制也会因遮挡或尺寸问题无法显示。
- 直接在
拖动时棋盘消失
- 拖动棋子触发重绘时,
JFrame的paint方法未正确处理组件层次,直接覆盖棋盘绘制;同时直接在JFrame上绘制棋盘不符合Swing组件设计规范,导致重绘顺序混乱。
- 拖动棋子触发重绘时,
修改后的完整代码
1. 棋盘面板(BoardPanel.java)
专门负责棋盘绘制的底层组件,替代直接在JFrame中绘制:
import javax.swing.JPanel; import java.awt.Graphics; import java.awt.Color; public class BoardPanel extends JPanel { public BoardPanel() { setSize(1000, 1000); setLayout(null); // 使用绝对布局,方便棋子定位 } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); int[] swap = {1,2}; String[] rowNum = {"8","7","6","5","4","3","2","1"}; String[] ColumnLetter = {"A","B","C","D","E","F","G","H"}; int rowCount = 0; int columnCount = 0; for(int i=100;i<900;i+=100) { for(int j=100;j<900;j+=100) { if(i==100) { g.setColor(Color.black); g.drawString(rowNum[rowCount++],i-20,j+60); } if(j==100) { g.setColor(Color.black); g.drawString(ColumnLetter[columnCount++],i+50,j+820); } if(swap[0] == 1) g.setColor(Color.white); else g.setColor(Color.black); swapColor(swap); g.fillRect(i,j,100,100); if(j==800) swapColor(swap); } } } private void swapColor(int[] swap) { int temp = swap[0]; swap[0] = swap[1]; swap[1] = temp; } }
2. 主窗口(BoardGUI.java)
将棋盘面板作为内容容器,棋子添加到棋盘面板中:
import javax.swing.JFrame; public class BoardGUI extends JFrame { public BoardGUI() { this.setSize(1000,1000); this.setTitle("Chess"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 初始化棋盘面板并设置为内容面板 BoardPanel boardPanel = new BoardPanel(); this.setContentPane(boardPanel); // 添加棋子到棋盘面板 PieceImage king = new PieceImage("./king.png",500,800); boardPanel.add(king); this.setVisible(true); } }
3. 棋子组件(PieceImage.java)
修复显示与拖动逻辑,确保初始可见且拖动不遮挡棋盘:
import javax.swing.ImageIcon; import javax.swing.JPanel; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.Graphics; import java.awt.Point; public class PieceImage extends JPanel { private ImageIcon image; private Point offset; // 鼠标点击位置相对于棋子左上角的偏移量 public PieceImage(String imagePath, int xPos, int yPos) { if(imagePath == null) return; this.image = new ImageIcon(imagePath); // 设置组件大小与图片一致 setSize(image.getIconWidth(), image.getIconHeight()); // 设置初始位置 setLocation(xPos, yPos); // 开启透明模式,避免遮挡棋盘 setOpaque(false); // 合并鼠标点击与拖动监听器 MouseAdapter mouseHandler = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { // 记录鼠标相对于棋子的偏移,避免拖动时棋子跳变 offset = e.getPoint(); } @Override public void mouseDragged(MouseEvent e) { // 获取鼠标在父容器(棋盘)中的位置,计算棋子新位置 Point parentMousePos = getParent().getMousePosition(); if (parentMousePos != null) { int newX = (int) parentMousePos.getX() - offset.x; int newY = (int) parentMousePos.getY() - offset.y; setLocation(newX, newY); } } }; addMouseListener(mouseHandler); addMouseMotionListener(mouseHandler); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (image != null) { // 从组件左上角绘制图片,匹配组件位置 image.paintIcon(this, g, 0, 0); } } }
关键修改说明
- 棋盘绘制分离:将棋盘绘制逻辑移到独立的
BoardPanel中,符合Swing组件层次设计,确保棋盘作为底层容器稳定显示。 - 棋子组件优化:设置透明模式、匹配图片尺寸、修复拖动坐标计算,保证棋子初始可见且拖动时不遮挡棋盘。
- 重绘逻辑修复:调用
super.paintComponent(g)确保组件背景与子组件正确渲染,避免重绘时内容丢失。
内容的提问来源于stack exchange,提问作者CuberBoy
相关产品推荐
相关产品推荐

