JFrame扫雷项目地雷单元格PNG无法渲染问题求助
扫雷项目地雷单元格图片不显示问题排查
我开发了一个基于JFrame的扫雷项目,使用PNG作为单元格图标,所有图片都放在img目录下。非地雷单元格能正常渲染,但地雷单元格的图片始终无法显示。已确认绘图方法被调用,地雷单元格的状态标记也正确,以下是完整可复现代码:
Play.java
import java.awt.BorderLayout; import java.awt.EventQueue; import javax.swing.JFrame; import javax.swing.JLabel; public class Play { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { // 游戏配置:行数、列数、单元格尺寸、地雷数量 int[] default_configs = {16,16,15,10}; Configuration.loadParameters(default_configs); JFrame frame = new JFrame(); JLabel statusbar = new JLabel("select a cell"); frame.add(statusbar, BorderLayout.SOUTH); frame.add(new Board(Configuration.ROWS, Configuration.COLS, Configuration.MINES, statusbar)); frame.setResizable(false); frame.pack(); frame.setTitle("Ekrem's Minefield"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); } }
Board.java
import javax.swing.JPanel; import javax.swing.JLabel; import java.awt.Graphics; import java.awt.Dimension; public class Board extends JPanel { private Minefield minefield; private JLabel status; private int height; private int width; private int mines; /** * 创建雷区、初始化参数、激活鼠标事件并设置面板尺寸的构造方法 */ public Board(int height, int width, int mines, JLabel statusbar) { this.height = height; this.width = width; this.mines = mines; this.minefield = new Minefield(height, width, mines); status = statusbar; setPreferredSize(new Dimension(Configuration.BOARD_WIDTH, Configuration.BOARD_HEIGHT)); addMouseListener(new MouseReader(this)); } /** * @param Graphics 对象 */ @Override public void paintComponent(Graphics g) { minefield.draw(g); } public Minefield getMinefield() { return this.minefield; } /** * 根据鼠标交互调整单元格状态的方法 */ public void mouseClickOnLocation(int x, int y, String button) { Object clickedCell = minefield.getCellByScreenCoordinates(x,y); if (clickedCell instanceof InfoCell) { if (button == "right") { if (((InfoCell)(clickedCell)).getStatus().equals("opnd")) { return; } else if (((InfoCell)(clickedCell)).getStatus().equals("mrkd")) { ((InfoCell)(clickedCell)).setStatus("cvrd"); } else { ((InfoCell)(clickedCell)).setStatus("mrkd"); } } else if (button == "left") { //minefield.openCells((InfoCell)(clickedCell)); } } if (clickedCell instanceof MineCell) { if (button == "right") { if(((MineCell)(clickedCell)).getStatus().equals("cvrd")) { ((MineCell)(clickedCell)).setStatus("mrkd"); } else{ ((MineCell)(clickedCell)).setStatus("cvrd"); } } else if (button == "left") { ((MineCell)(clickedCell)).setStatus("opnd"); } } } }
Configuration.java
public class Configuration { public static int ROWS; public static int COLS; public static int CELL_SIZE; public static int MINES; public static int BOARD_WIDTH; public static int BOARD_HEIGHT; public static int UNOPENED_INFOCELL_COUNT; public static void loadParameters(int[] parameters){ ROWS = parameters[0]; COLS = parameters[1]; CELL_SIZE = parameters[2]; MINES = parameters[3]; UNOPENED_INFOCELL_COUNT = ROWS * COLS - MINES; BOARD_WIDTH = COLS * CELL_SIZE + 1; BOARD_HEIGHT = ROWS * CELL_SIZE + 1; } }
InfoCell.java
import javax.swing.ImageIcon; import java.awt.Graphics; import java.awt.Image; public class InfoCell { private int row; private int column; private int numOfAdjacentMines = 0; private String status = "cvrd"; // 覆盖、未覆盖、标记、错误标记 public InfoCell(int row, int column, int numOfAdjacentMines) { this.row = row; this.column = column; this.numOfAdjacentMines = numOfAdjacentMines; } public int getRow() { return this.row; } public int getCol() { return this.column; } public void setMines(int mineCount) { this.numOfAdjacentMines = mineCount; } public int getNumOfAdjacentMines() { return this.numOfAdjacentMines; } public void draw(Graphics g) { g.drawImage(this.getImage(), this.getHorizontalPosition(), this.getVerticalPosition(), null); } public Image getImage() { Image image; if (status.equals("cvrd")) { ImageIcon a = new ImageIcon("img/covered_cell.png"); image = a.getImage(); } else if(status.equals("mrkd")) { ImageIcon a = new ImageIcon("img/marked_cell.png"); image = a.getImage(); } else if(status.equals("wmrkd")){ ImageIcon a = new ImageIcon("img/wrong_mark.png"); image = a.getImage(); } else /*(status.equals(Configuration.STATUS_OPENED))*/ { ImageIcon a = new ImageIcon("img/info_" + this.numOfAdjacentMines + ".png"); image = a.getImage(); } return image; } public int getHorizontalPosition() { return (column) * Configuration.CELL_SIZE; // 计算并返回单元格左上角的水平像素位置 } public int getVerticalPosition() { return (row) * Configuration.CELL_SIZE; // 计算并返回单元格左上角的垂直像素位置 } public String getStatus() { return this.status; } public void setStatus(String status) { // 单元格状态的设置方法,请勿硬编码值,请使用Configuration参数 this.status = status; } }
MineCell.java
import javax.imageio.ImageIO; import javax.swing.ImageIcon; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.awt.Graphics; /** * * @author Ekrem Kaya * @version 9:44 03/11 */ public class MineCell { /** * 每个单元格的行、列和状态 */ private int row; private int column; private String status = "cvrd"; // 覆盖 - cvrd,未覆盖 - opnd,标记 - mrkd /** * * @param row 单元格在雷区中的垂直位置 * @param column 单元格在雷区中的水平位置 */ public MineCell(int row, int column) { this.row = row; this.column = row; } /** * * @param g 雷区类传递给单元格的Graphics对象 */ public void draw(Graphics g) { g.drawImage(this.getImage(), this.getHorizontalPosition(), this.getVerticalPosition(), null); } /** * * @return 返回表示水平像素位置的整数 */ public int getHorizontalPosition() { return (column) * Configuration.CELL_SIZE; // 计算并返回单元格左上角的水平像素位置 } /** * * @return 返回表示垂直像素位置的整数 */ public int getVerticalPosition() { return (row) * Configuration.CELL_SIZE; // 计算并返回单元格左上角的垂直像素位置 } /** * * @return 返回单元格的状态属性,可选值:覆盖、打开、错误标记、标记 */ public String getStatus() { return this.status; } /** * * @param status 表示单元格状态的字符串 */ public void setStatus(String status) { // 单元格状态的设置方法,请勿硬编码值,请使用Configuration参数 this.status = status; } /** * * @return 返回代表单元格状态的图片(来自文件夹) */ public Image getImage() { Image image; if(this.status.equals("mrkd")) { ImageIcon a = new ImageIcon("img/marked_cell.png"); image = a.getImage(); } else if(this.status.equals("opnd")) { ImageIcon a = new ImageIcon("img/mine_cell.png"); image = a.getImage(); } else /*(status.equals(Configuration.STATUS_COVERED))*/ { ImageIcon a = new ImageIcon("img/covered_cell.png"); image = a.getImage(); } return image; } }
MouseReader.java
/** * 请勿修改此文件中的任何内容 */ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; public class MouseReader extends MouseAdapter { private Board board; public MouseReader(Board board) { this.board = board; } @Override public void mouseClicked(MouseEvent e) { String button = ""; if (e.getButton() == MouseEvent.BUTTON1) button = "left"; else if (e.getButton() == MouseEvent.BUTTON3) button = "right"; board.mouseClickOnLocation(e.getX(), e.getY(), button); } }
问题根源
看MineCell的构造函数:
public MineCell(int row, int column) { this.row = row; this.column = row; // 这里错误地把row赋值给了column! }
这个bug导致所有地雷单元格的column值和row完全相同,意味着地雷都被绘制在了超出棋盘范围的位置,或者被其他单元格完全覆盖,所以看起来像是没有显示。
修复方法
把MineCell构造函数里的赋值修正:
public MineCell(int row, int column) { this.row = row; this.column = column; // 正确赋值column参数 }
额外优化建议
- 字符串比较用
equals()而非==:比如Board类里的button == "right"应该改成"right".equals(button),避免空指针问题。 - 状态变化后刷新界面:在
Board的mouseClickOnLocation方法末尾加上this.repaint();,否则单元格状态变化不会立刻显示在界面上。
内容的提问来源于stack exchange,提问作者jerome utti
相关产品推荐
相关产品推荐

