简体   繁体   English

在方法中初始化后如何正确显示JFrame

[英]How to correctly show a JFrame after initializing it in a method

This is a long question, so bear with me for a minute. 这是一个很长的问题,请耐心等待一分钟。 I'm making a Game of Life application which first shows a JFrame with a grid of JButtons. 我正在制作一个Game of Life应用程序,该应用程序首先显示带有JButtons网格的JFrame。 If you click on a JButton its background turns black and when activated again, its background turns back to white. 如果单击JButton,其背景将变为黑色,再次激活后,其背景将变为白色。 My code: 我的代码:

public class Choose implements ActionListener {
public final static int DIMENSION = 50;
Color BLACK = new Color(0,0,0);
Color WHITE = new Color(255,255,255);
JFrame choose;
JButton[] choice;
JButton clear, fill, go;
JPanel baseChoose, baseFrame, buttonsAndText;
GridLayout base;

public Choose() {
    choose = new JFrame("Make your own game");
    choice = new JButton[DIMENSION*DIMENSION];

    baseChoose = new JPanel();
        baseChoose.setSize(500, 500);
    buttonsAndText = new JPanel();

    buttonsAndText.add(clear = new JButton("Clear"));
        clear.addActionListener(this);
    buttonsAndText.add(fill = new JButton("Fill"));
        fill.addActionListener(this);
    buttonsAndText.add(go = new JButton("Go"));
        go.addActionListener(this);

    base = new GridLayout(DIMENSION, DIMENSION);
        base.setHgap(-1);
        base.setVgap(-1);

    baseChoose.setLayout(base);
    choose.add(baseChoose);
    choose.add(buttonsAndText);

    JLabel text = new JLabel("Press 'Go' to start.");
    buttonsAndText.add(text);

    for (int i = 0; i < (DIMENSION*DIMENSION); i++) {
        baseChoose.add(choice[i] = new JButton());
        choice[i].setBackground(WHITE);
        choice[i].addActionListener(this);
    }

    choose.setSize(500, 800);
    choose.setVisible(true);
    choose.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    choose.setResizable(false);
}

public void actionPerformed(ActionEvent e) {
    JButton b = (JButton)e.getSource();

    for (int i = 0; i < (DIMENSION*DIMENSION); i++) {
        if (b == choice[i]) {
            if (b.getBackground() == BLACK) {
                choice[i].setBackground(WHITE);
            } else if (b.getBackground() == WHITE) {
                choice[i].setBackground(BLACK);
            }
        }
    }

    if (b == clear) {
        for (int i = 0; i < (DIMENSION*DIMENSION); i++) {
            choice[i].setBackground(WHITE);
        }
        choose.validate();
        choose.repaint();
    }

    if (b == fill) {
        for (int i = 0; i < (DIMENSION*DIMENSION); i++) {
            choice[i].setBackground(BLACK);
        }
        choose.validate();
        choose.repaint();
    }

    if (b == go) {
        int states[] = new int[DIMENSION*DIMENSION];
        for (int i = 0; i < DIMENSION*DIMENSION; i++) {
            System.out.println(choice[i].getBackground() == BLACK);
            if (choice[i].getBackground() == BLACK) {
                states[i] = 1;
            } else if (choice[i].getBackground() == WHITE) {
                states[i] = 0;
            }
        }
        choose.dispose();
        Gui own = new Gui(states);
    }
}
}

In the main method I make an instance of this class, when you have chosen your buttons to activate, you can click the go button to actually show the Game of Life. 在主要方法中,我创建了该类的一个实例,当您选择要激活的按钮时,可以单击“ go按钮以实际显示“生命游戏”。

public class Gui  {
public final static int DIMENSION = 50;
Color BLACK = new Color(0,0,0);
Color WHITE = new Color(255,255,255);
JFrame frame, ownFrame;
JPanel baseFrame;
GridLayout base;

public Gui(int[] states) {
    frame = new JFrame("Game of Life by Boris Verwoerd");

    baseFrame = new JPanel();
        baseFrame.setSize(500, 500);

    frame.add(baseFrame);

    base = new GridLayout(DIMENSION, DIMENSION);
        base.setHgap(-1);
        base.setVgap(-1);

    baseFrame.setLayout(base);

    JPanel[] box = new JPanel[DIMENSION*DIMENSION];

    for (int i = 0; i < (DIMENSION*DIMENSION); i++) {
        baseFrame.add(box[i] = new JPanel());
        box[i].setBorder(BorderFactory.createLineBorder(Color.black));
    }

    frame.setSize(500, 500);
    frame.setVisible(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setResizable(false);
    frame.validate();
    frame.repaint();

    gameLoop(box, frame, states);
}

private static void gameLoop(JPanel[] boxes, JFrame theFrame, int[] states) {
    int[] newstates = new int[DIMENSION*DIMENSION];
    for (int i = 0; i < 20; i++) {

        newstates = Maths.render(boxes, theFrame, states);
        states = newstates;
        theFrame.validate();
        theFrame.repaint();

        try {
            Thread.sleep(100);                 //1000 milliseconds is one second.
        } catch(InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
    }
}

public static int getDim() {
    return DIMENSION;
}

}

When pressing go, a new JFrame launches but the screen is totally white, only after the gameLoop has ended it shows the JPanel grid. 按下go键时,会启动一个新的JFrame,但屏幕完全是白色的,仅在gameLoop结束后才显示JPanel网格。

Only when I make an instance of Gui from the main method with an all zero array states it shows it correctly. 只有当我从main方法以全零数组states Gui实例时,它才能正确显示它。 I've tried so many things to come up with a solution but I can't understand why it causes this white screen instead of the Grid. 我已经尝试了很多方法来提出解决方案,但是我不明白为什么它会导致白色屏幕而不是网格。 Hence my question is: How can I show my instance of Gui correctly, without the white screen when initialized from a method? 因此,我的问题是:从方法初始化时,如何在没有白屏的情况下正确显示Gui的实例?

  • Sorry for the long post/code! 抱歉,很长的帖子/代码!

Edit: 编辑:

This is my Maths class: 这是我的数学课:

public class Maths {
static Color BLACK = new Color(0,0,0);
static Color WHITE = new Color(255,255,255);

public static int[] render(JPanel[] box, JFrame frame, int[] state) {
    int[] newstates = new int[Gui.getDim()*Gui.getDim()];
    for (int i = 0; i < (Gui.getDim()*Gui.getDim()); i++) newstates[i] = 0;

    for (int i = (Gui.getDim()+1); i < (Gui.getDim()*Gui.getDim() - (Gui.getDim()+1)); i++) {
        if (state[i] == 1) {
            int aliveNeighbours = 0;
            if (state[i-(Gui.getDim()+1)] == 1) aliveNeighbours++;
            if (state[i-Gui.getDim()] == 1) aliveNeighbours++;
            if (state[i-(Gui.getDim()-1)] == 1) aliveNeighbours++;
            if (state[i-1] == 1) aliveNeighbours++;
            if (state[i+1] == 1) aliveNeighbours++;
            if (state[i+(Gui.getDim()-1)] == 1) aliveNeighbours++;
            if (state[i+Gui.getDim()] == 1) aliveNeighbours++;
            if (state[i+(Gui.getDim()+1)] == 1) aliveNeighbours++;

            if (aliveNeighbours == 2 || aliveNeighbours == 3) {
                box[i].setBackground(BLACK);
                newstates[i] = 1;
            } else {
                box[i].setBackground(WHITE);
                newstates[i] = 0;
            }

        } else if (state[i] == 0) {
            int aliveNeighbours = 0;
            if (state[i-(Gui.getDim()+1)] == 1) aliveNeighbours++;
            if (state[i-Gui.getDim()] == 1) aliveNeighbours++;
            if (state[i-(Gui.getDim()-1)] == 1) aliveNeighbours++;
            if (state[i-1] == 1) aliveNeighbours++;
            if (state[i+1] == 1) aliveNeighbours++;
            if (state[i+(Gui.getDim()-1)] == 1) aliveNeighbours++;
            if (state[i+Gui.getDim()] == 1) aliveNeighbours++;
            if (state[i+(Gui.getDim()+1)] == 1) aliveNeighbours++;

            if (aliveNeighbours == 3) {
                box[i].setBackground(BLACK);
                newstates[i] = 1;
            } else {
                box[i].setBackground(WHITE);
                newstates[i] = 0;
            }
        }
    }
    return newstates;
}
}

Yours is a classic case of a Swing threading issue, with here it being due to your having long-running code that also calls Thread.sleep(...) on the Swing event thread, which will put the entire GUI to sleep, certainly not your goal. 您的问题是Swing线程问题的典型案例,这是由于您的代码运行时间很长,还需要在Swing事件线程上调用Thread.sleep(...) ,这肯定会使整个GUI进入睡眠状态。不是你的目标。

The solution is the same as any similar question (which you should have searched for and found before posting here): use a SwingWorker for background threading if you have long-running code, or a Swing Timer for calling code recurrently with delays. 该解决方案与任何类似的问题相同(在发布之前,应已搜索并找到了类似的问题):如果您的代码运行时间较长,请使用SwingWorker进行后台线程处理,或者使用Swing计时器以延迟的方式循环调用代码。

Here which you use will depend on how slow Maths.render is. 在此使用的方法取决于Maths.render速度。 If this calculates very fast, then all you need is a Swing Timer to make this call intermittently and with delay. 如果计算速度非常快,那么您只需要一个Swing计时器即可间歇地且延迟地进行此调用。 If this method takes a significant amount of time to perform, then you will need to go the SwingWorker route. 如果此方法花费大量时间执行,那么您将需要执行SwingWorker路线。

For example, timer code could look like: 例如,计时器代码如下所示:

private void gameLoop() {
  int timerDelay = 100;
  new Timer(timerDelay, new ActionListener() {
     private final int maxIndex = 20;
     private int index = 0;

     @Override
     public void actionPerformed(ActionEvent e) {
        if (index < maxIndex) {
           states = Maths.render(box, frame, states);
           frame.validate();
           frame.repaint();
      } else {
           ((Timer) e.getSource()).stop();
        }
        index++;
     }
  }).start();
}

Note that gameLoop should be a non-static instance method. 注意gameLoop应该是一个非静态实例方法。 I also did not allow parameter passage, but instead made these guys fields of the class: 我也不允许参数传递,而是使该类的这些家伙领域:

private JPanel[] box;
private int[] states;

and made sure to set those fields: 并确保设置以下字段:

public Gui(int[] states) {
  this.states = states;
  frame = new JFrame("Game of Life by Boris Verwoerd");

  baseFrame = new JPanel();
  baseFrame.setSize(500, 500);

  frame.add(baseFrame);

  base = new GridLayout(DIMENSION, DIMENSION);
  base.setHgap(-1);
  base.setVgap(-1);

  baseFrame.setLayout(base);

  box = new JPanel[DIMENSION * DIMENSION];

  for (int i = 0; i < (DIMENSION * DIMENSION); i++) {
     baseFrame.add(box[i] = new JPanel());
     box[i].setBorder(BorderFactory.createLineBorder(Color.black));
  }

  frame.setSize(500, 500);
  frame.setVisible(true);
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  frame.setResizable(false);
  frame.validate();
  frame.repaint();

  gameLoop();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM