单击重置按钮时,我试图重置我的生活游戏版本,但是我遇到了问题。
单击该按钮后,所有内容均成功重置,但无法看到主要的Jpanel世代在移动。
我有两个JLabel,一个显示当前世代的数量,另一个显示那个世代中活细胞的数量。它们都已成功重置,但是主JPanel只是冻结了,我再也看不到动画了。

GameOfLife类:

public class GameOfLife extends JFrame implements ActionListener {

private static class GameStep extends TimerTask {
    static GameOfLife life = new GameOfLife();

    @Override
    public void run() {
        updateLabels();
    }
}

static JLabel aliveLabel = new JLabel("Alive:");
static JLabel GenerationLabel = new JLabel("Generation #");
static CellGrid body = new CellGrid();
static JPanel header = new JPanel();
static int genNumber = 1;
static JButton PlayToggleButton = new JButton("pause");
static JButton ResetButton = new JButton("reset");
static Boolean isPaused = false;
static GameStep game = new GameStep();
static Timer timer = new Timer();


public GameOfLife() {
    super("Game of life");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(700, 660);
    setLocationRelativeTo(null);
    setLayout(new FlowLayout());

    GenerationLabel.setName("GenerationLabel");
    aliveLabel.setName("aliveLabel");
    PlayToggleButton.setName("PlayToggleButton");
    ResetButton.setName("ResetButton");

    PlayToggleButton.addActionListener(this);
    ResetButton.addActionListener(this);

    PlayToggleButton.setIcon(new ImageIcon(play));
    ResetButton.setIcon(new ImageIcon(reset));

    PlayToggleButton.setPreferredSize(new Dimension(40,30));
    ResetButton.setPreferredSize(new Dimension(40,30));

    header.setLayout(new FlowLayout());
    header.setPreferredSize(new Dimension(100, this.getHeight()));
    header.add(PlayToggleButton);
    header.add(ResetButton);
    header.add(GenerationLabel);
    header.add(aliveLabel);

    body.setLayout(new BorderLayout());
    body.setPreferredSize(new Dimension(500, this.getHeight()));

    add(header, BorderLayout.WEST);
    add(body, BorderLayout.CENTER);
    setVisible(true);

}

public static void updateLabels(){
    body.run();
    GenerationLabel.setText("Generation #"+ genNumber++);
    aliveLabel.setText("Alive: "+ body.totalAlive());
}

@Override
public void actionPerformed(ActionEvent e) {

    if(e.getActionCommand().equals("pause")){
        pauseResume();
    }
    else if(e.getActionCommand().equals("reset")){
        reset();
    }
}

static void loopStep(){
    timer.schedule(game, 0,1000);
}

static void pauseResume() {

    if(!isPaused){
        isPaused = true;
        timer.cancel();
    }
    else{
        isPaused = false;
        timer = new Timer();
        timer.schedule(new GameStep(), 0,1000);
    }
}
static void reset() {
    timer.cancel();
    isPaused = false;

    genNumber = 1;
    header = new JPanel();
    body = new CellGrid();
    body.repaint();

    timer = new Timer();
    timer.schedule(new GameStep(), 0,1000);
}

public static void main(String[] args) {
   loopStep();
}
}


CellGrid类:

public class CellGrid extends JPanel implements Runnable{
private static final int ROWS = 60;
private static final int COLS = 60;
private static final int CELL_WIDTH = 10;
private static Cell[][] cellGrid = new Cell[ROWS][COLS];

public CellGrid() {
    for (int row = 0; row < cellGrid.length; row++) {
        for (int col = 0; col < cellGrid[row].length; col++) {
            int x = col * CELL_WIDTH;
            int y = row * CELL_WIDTH;
            cellGrid[row][col] = new Cell(x, y, CELL_WIDTH);

            if (new Random().nextBoolean()) {
                cellGrid[row][col].setAlive(true);
            } else {
                cellGrid[row][col].setAlive(false);
            }
        }
    }
}
public int totalAlive(){
    int totalAlive = 0;
    for (Cell[] cells : cellGrid) {
        for (int j = 0; j < cellGrid.length; j++) {
            if (cells[j].isAlive())
                totalAlive++;
        }
    }
    return totalAlive;
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    for (Cell[] cellRow : cellGrid) {
        for (Cell cell : cellRow) {
            cell.draw(g2);
        }
    }
}

@Override
public void run() {
    cellGrid = new GenerationMaker4().nextGeneration(cellGrid);
    repaint();
}
}


知道为什么会这样吗?

最佳答案

您的reset()方法:

static void reset() {
    timer.cancel();
    isPaused = false;

    genNumber = 1;
    header = new JPanel();
    body = new CellGrid();
    body.repaint();

    timer = new Timer();
    timer.schedule(new GameStep(), 0,1000);
}


问题是常见的新手错误-您认为更改变量引用将更改该变量最初引用的先前对象。

具体来说,您有body = new CellGrid();,它的工作是使body变量引用一个新的CellGrid对象,但是(这是重要的部分),它*对GUI中当前显示的CellGrid对象没有任何作用,之前提到的body变量。

几种替代解决方案:


现在将body变量中现在引用的新CellGrid对象添加到GUI的同一BorderLayout位置,覆盖上一个对象
更好的方法是不创建新的CellGrid对象,而是创建一种将当前CellGrid设置回其初始状态的方法。


例如,如果您将CellGrid更改为...

public class CellGrid extends JPanel implements Runnable{
    private static final int ROWS = 60;
    private static final int COLS = 60;
    private static final int CELL_WIDTH = 10;
    private Cell[][] cellGrid = new Cell[ROWS][COLS]; // make this non-static

    public CellGrid() {
        reset();
    }

    public void reset() {
        cellGrid = new Cell[ROWS][COLS];
        for (int row = 0; row < cellGrid.length; row++) {
            for (int col = 0; col < cellGrid[row].length; col++) {
                int x = col * CELL_WIDTH;
                int y = row * CELL_WIDTH;
                cellGrid[row][col] = new Cell(x, y, CELL_WIDTH);

                if (new Random().nextBoolean()) {
                    cellGrid[row][col].setAlive(true);
                } else {
                    cellGrid[row][col].setAlive(false);
                }
            }
        }
    }

// ..... more code below


然后,您要做的就是在当前CellGrid对象上调用reset(),然后调用repaint()

其他事宜:


您严重使用了static修饰符。除了main方法,常量之外,该程序中的任何内容都不应该是静态的,仅此而已。这对于这个小程序可能并不重要,但是在稍后尝试进行单元测试或扩展或增强此程序,或将其添加到另一个更大的程序中时,这将变得很重要。
您使用java.util.Timerjava.util.TimerTask在Swing GUI程序中运行动画循环,这样做并不安全,因为这些类不是Swing线程安全的。最好使用javax.swing.Timer或“ Swing Timer”代替这两个类来运行动画,因为这对于此GUI库是线程安全的。

10-06 10:09