单击重置按钮时,我试图重置我的生活游戏版本,但是我遇到了问题。
单击该按钮后,所有内容均成功重置,但无法看到主要的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.Timer
和java.util.TimerTask
在Swing GUI程序中运行动画循环,这样做并不安全,因为这些类不是Swing线程安全的。最好使用javax.swing.Timer
或“ Swing Timer”代替这两个类来运行动画,因为这对于此GUI库是线程安全的。