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

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参数
}

额外优化建议

  1. 字符串比较用equals()而非==:比如Board类里的button == "right"应该改成"right".equals(button),避免空指针问题。
  2. 状态变化后刷新界面:在Board的mouseClickOnLocation方法末尾加上this.repaint();,否则单元格状态变化不会立刻显示在界面上。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 16:37:00