【问题标题】:How to correctly show a JFrame after initializing it in a method如何在方法中初始化 JFrame 后正确显示它
【发布时间】:2014-12-24 11:43:55
【问题描述】:

这是一个很长的问题,请耐心等待。我正在制作一个 Game of Life 应用程序,它首先显示一个带有 JButton 网格的 JFrame。如果单击 JButton,它的背景会变黑,当再次激活时,它的背景会变回白色。我的代码:

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);
    }
}
}

在我创建这个类的一个实例的 main 方法中,当您选择要激活的按钮后,您可以单击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;
}

}

按下 go 时,会启动一个新的 JFrame,但屏幕是全白的,只有在 gameLoop 结束后才会显示 JPanel 网格。

只有当我使用全零数组states 从主方法创建 Gui 实例时,它才能正确显示。 我已经尝试了很多方法来提出解决方案,但我不明白为什么它会导致这个白屏而不是网格。因此我的问题是:如何在从方法初始化时正确显示我的 Gui 实例,而不会出现白屏?

  • 抱歉,帖子/代码太长了!

编辑:

这是我的数学课:

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;
}
}

【问题讨论】:

    标签: java swing awt


    【解决方案1】:

    您的问题是 Swing 线程问题的典型案例,这是由于您的长时间运行的代码还在 Swing 事件线程上调用 Thread.sleep(...),这将使整个 GUI 进入睡眠状态,当然不是您的目标。

    解决方案与任何类似问题相同(您应该在此处发布之前搜索并找到该问题):如果您有长时间运行的代码,请使用 SwingWorker 进行后台线程,或者使用 Swing Timer 调用代码经常延迟。

    您在此处使用哪一个取决于Maths.render 的速度有多慢。如果这计算得非常快,那么您只需要一个 Swing Timer 来间歇性地和延迟地进行此调用。如果此方法需要大量时间来执行,那么您将需要使用 SwingWorker 路线。

    例如,计时器代码可能如下所示:

    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();
    }
    

    请注意,gameLoop 应该是一个非静态实例方法。我也不允许参数传递,而是将这些家伙作为类的字段:

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

    并确保设置这些字段:

    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();
    }
    

    【讨论】:

    • 感谢您的回复,我确实尝试过用谷歌搜索但从未想到过这个。你能看看我的数学课并确定它是否快吗?我仍然是一个初学者程序员,我不知道这个类是快还是慢。
    • @BorisVerwoerd:请检查编辑以回答。你将不得不测试你的代码,看看它是快还是慢。
    • 试过了,效果很好,我从没想过我的方法运行得很慢,但我不知道什么时候被认为是慢或快。感谢您的回答,我今天学到了新东西!
    猜你喜欢
    • 1970-01-01
    • 2019-10-24
    • 2019-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-05
    • 1970-01-01
    相关资源
    最近更新 更多