简体   繁体   English

java.util.Timer && Swing 事故

[英]java.util.Timer && Swing mishaps

Currently, I am making a turn based game that has a JLabel with time remaining, several other labels for question/answer/team scores, and a main panel with the game board.目前,我正在制作一个回合制游戏,它有一个带有剩余时间的 JLabel、几个其他的问题/答案/团队分数标签,以及一个带有游戏板的主面板。

First issue with this code: The GameTimer class is called too often, IE it isn't being called every second, but about 5-10 times per second.这段代码的第一个问题:GameTimer 类被调用得太频繁,IE 它不是每秒被调用,而是每秒大约 5-10 次。 (The print statement that outputs "Timer: " + timeElapsed is printing every number up to around 80 (Timer: 1, Timer: 2, etc.) within about 10 seconds. As a side note, the time for the question never exceeds 15 seconds. Shouldn't the while loop stop before it goes haywire? This issue happens only on occasion. I assume it's a util.Timer related bug, not in my code, but I'm not too familiar with the inner workings of the class. (输出 "Timer: " + timeElapsed 的打印语句在大约 10 秒内打印每个数字,最多可达 80(定时器:1,定时器:2 等)。作为旁注,问题的时间永远不会超过 15秒。while 循环不应该在它失控之前停止吗?这个问题只偶尔发生。我认为这是一个 util.Timer 相关的错误,不在我的代码中,但我对类的内部工作不太熟悉.

Second issue: The for:each loop is called in an infinite loop, as in it prints "Reached Questions. Number of questions: " + questions.size() and continues to the for:each loop indefinitely.第二个问题:for:each 循环在无限循环中被调用,因为它打印“Reached Questions. Number of questions:” + questions.size() 并无限期地继续 for:each 循环。 I didn't think windowOpened() could be called multiple times, and I also used windowActivated() with the same results.我不认为 windowOpened() 可以被多次调用,而且我还使用了 windowActivated() 并得到了相同的结果。

Final issue: The time label is only updated after timeElapsed exceeds question.getTime, meaning that the label will only have a negative number in it.最后一个问题:时间标签只有在 timeElapsed 超过 question.getTime 后才会更新,这意味着标签中只会有一个负数。 The while loop should detect that timeElapsed is greater, and stop, but it doesn't. while 循环应该检测到 timeElapsed 更大,然后停止,但事实并非如此。

I'm bewildered by all of these, any help is greatly appreciated.我对所有这些感到困惑,非常感谢任何帮助。

A little history of the project: Before implementing windowListener, the class just waited 30 seconds before executing the for:each loop, which did not work because the window would be a gray screen (unloaded) until the timer was finished, and then it would load normally, but GameTimer started immediately after the 30 second timer.项目的一点历史:在实现 windowListener 之前,该类只是在执行 for:each 循环之前等待了 30 秒,这不起作用,因为在计时器完成之前窗口将是灰屏(卸载),然后它会正常加载,但 GameTimer 在 30 秒计时器后立即启动。 I then tried using a Thread to update itself every second, but that had similar issues to the previous attempt.然后我尝试使用 Thread 每秒更新一次,但这与之前的尝试有类似的问题。

There are other classes other than this.除此以外还有其他类。 These 2 are the ones I believe the errors are inside of:这两个是我认为错误在其中的:

The game screen code:游戏画面代码:

import java.awt.*;
import javax.swing.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import java.util.*;
import java.awt.event.*;

@SuppressWarnings("deprecation")
public class GameScreen implements WindowListener{
    private int team1Score = 0;
    private int team2Score = 0;
    private ArrayList<Question> questions;

    private Question cQuestion;

    private boolean start = false;

    private boolean timesUp = false;
    private boolean isCorrect = false;
    private java.util.Timer timer = new java.util.Timer();
    private int timeElapsed = 0;

    private boolean isTeam1 = true;

    private String correctAnswer = "";
    private String inputAnswer = "";
    private int numIncorrect = 0;

    private TopPanel topPanel = new TopPanel();

    public GameScreen(ArrayList<Question> questions) {
        JFrame gameFrame = new JFrame("Cavazos Math Game");

        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        int width = (int)screenSize.getWidth();
        int height = (int)screenSize.getHeight();

        gameFrame.setSize(width, height);
        gameFrame.setResizable(false);
        gameFrame.setLocationRelativeTo(null);
        gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        topPanel.setSize(width, 200);

        this.questions = questions;

        BorderLayout layout = new BorderLayout();

        gameFrame.setLayout(layout);

        System.out.println("Reached Image Loading");

        try {
            BufferedImage fieldImage = ImageIO.read(new File("field2.png"));
            GamePanel gamePanel = new GamePanel(fieldImage);
            gamePanel.setSize(width, height - 200);
            gameFrame.add(gamePanel, BorderLayout.CENTER);
        }
        catch(Exception e) {System.out.println("RUH ROH");}

        System.out.println("Reached Layout Loading");

        gameFrame.add(topPanel, BorderLayout.NORTH);
        gameFrame.setVisible(true);

        gameFrame.addKeyListener(topPanel);

        gameFrame.addWindowListener(this);
    }

    public void windowDeactivated(WindowEvent e) {}

    public void windowIconified(WindowEvent e) {}

    public void windowDeiconified(WindowEvent e) {}

    public void windowClosed(WindowEvent e) {}

    public void windowClosing(WindowEvent e) {}

    public void windowActivated(WindowEvent e) {}

    public void windowOpened(WindowEvent e) {
        System.out.println("Reached Questions. Number of question: " + questions.size());

        for(Question question : questions) {
            timeElapsed = 0;
            cQuestion = question;
            timesUp = false;
            topPanel.setQuestion(question.getQuestionString());
            setTimer(question.getTime());
            correctAnswer = question.getAnswer();
            System.out.println(question.getTime() + "");
            while((timeElapsed < question.getTime()) && !isCorrect && (numIncorrect <= 4)) {
                if(!topPanel.getInput().equals("nothing")) { //has an answer been submitted?
                    inputAnswer = topPanel.getInput();
                    if(!inputAnswer.equals(correctAnswer)) { //is it the wrong answer
                        numIncorrect++;
                        topPanel.setAnswer("");
                    }
                    else {
                        numIncorrect = 0;
                        isCorrect = true;
                        if(isTeam1) {
                            team1Score += question.getPoints();
                        }
                        else {
                            team2Score += question.getPoints();
                        }
                    }
                }
                else {

                }
                topPanel.setTimer(cQuestion.getTime() - timeElapsed);
            }
            isTeam1 = !isTeam1;
        }
    }

    class GameTimer extends TimerTask {
        public void run() {
            System.out.println("Timer: " + timeElapsed);
            timeElapsed++;
            topPanel.setTimer(cQuestion.getTime() - timeElapsed);
        }
    }

    public void setTimer(int seconds) {
        System.out.println("Timer has started");
        timer = new java.util.Timer();
        timer.schedule(new GameTimer(), 0, 1000);
    }
}

Top panel code:顶部面板代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class TopPanel extends JPanel implements KeyListener {
    JLabel questionLabel = new JLabel("Test Question");
    JTextField answerLabel = new JTextField("Answer label");
    JTextField timer = new JTextField("Timer Label");
    JTextField team1 = new JTextField("Team 1: 0");
    JTextField team2 = new JTextField("Team 2: 0");

    JPanel teamScores = new JPanel();

    //timer.setEditable(false);
    //team1.setEditable(false);
    //team2.setEditable(false);
    //answerLabel.setEditable(false);

    String inputString = "";
    boolean doneInputting = false;

    GridLayout scoresLayout = new GridLayout(2,1);
    GridLayout topLayout = new GridLayout(1,4);

    public TopPanel() {
        teamScores.setLayout(scoresLayout);
        teamScores.add(team1);
        teamScores.add(team2);
        this.setLayout(topLayout);
        this.setSize(getWidth(), 100);
        this.add(questionLabel);
        this.add(answerLabel);
        this.add(timer);
        this.add(teamScores);

        Font topFont = new Font("Sans Serif", Font.PLAIN, 32);

        this.setFont(topFont);
    }

    public void setQuestion(String question) {
        questionLabel.setText(question);
    }

    public void setAnswer(String answer) {
        answerLabel.setText(answer);
    }

    public void setTimer(int time) {
        timer.setText(time + "");
    }

    public void setTeam1Score(int score) {
        team1.setText(score + "");
    }

    public void addTeam1Score(int score) {
        team1.setText(score + "");
    }

    public void setTeam2Score(int score) {
        team2.setText(score + "");
    }

    public String getInput() {
        if(doneInputting) {
            return inputString;
        }
        return "nothing";
    }

    public String getCurrentInput() {
        return inputString;
    }

    public void keyPressed(KeyEvent e) {

    }

    public void keyReleased(KeyEvent e) {

    }

    public void keyTyped(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_9) {
            inputString += "9";
        }
        else if(e.getKeyCode() == KeyEvent.VK_8) {
            inputString += "8";
        }
        else if(e.getKeyCode() == KeyEvent.VK_7) {
            inputString += "7";
        }
        else if(e.getKeyCode() == KeyEvent.VK_6) {
            inputString += "6";
        }
        else if(e.getKeyCode() == KeyEvent.VK_5) {
            inputString += "5";
        }
        else if(e.getKeyCode() == KeyEvent.VK_4) {
            inputString += "4";
        }
        else if(e.getKeyCode() == KeyEvent.VK_3) {
            inputString += "3";
        }
        else if(e.getKeyCode() == KeyEvent.VK_2) {
            inputString += "2";
        }
        else if(e.getKeyCode() == KeyEvent.VK_1) {
            inputString += "1";
        }
        else if(e.getKeyCode() == KeyEvent.VK_0) {
            inputString += "0";
        }
        else if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
            inputString = inputString.substring(0, inputString.length()-1);
        }
        else if(e.getKeyCode() == KeyEvent.VK_T) {
            inputString += "TRUE";
        }
        else if(e.getKeyCode() == KeyEvent.VK_F) {
            inputString += "FALSE";
        }
        else if(e.getKeyCode() == KeyEvent.VK_P) {
            inputString += "π";
        }
        else if(e.getKeyCode() == KeyEvent.VK_S) {
            inputString += "√";
        }
        else if(e.getKeyCode() == KeyEvent.VK_ENTER) {
            doneInputting = true;
        }
        else if(e.getKeyCode() == KeyEvent.VK_SLASH) {
            inputString += "/";
        }
        setAnswer(inputString);
    }
}

You're starting a new Timer object with each iteration of your while loop, and so this is advancing your timeElapsed field with extra speed, each time a new Timer instance is created within the loop.您在 while 循环的每次迭代中都会启动一个新的 Timer 对象,因此每次在循环内创建一个新的 Timer 实例时,都会以额外的速度推进您的 timeElapsed 字段。 The solution is to not do this, to only use one Timer, or to stop a Timer before creating a new one,.... but having said this, you've other significant problems in your code including:解决方案是不这样做,只使用一个 Timer,或者在创建一个新的 Timer 之前停止一个 Timer,....但是话虽如此,您的代码中还有其他重大问题,包括:

  • This is a Swing application and you shouldn't be using java.util.Timers but rather javax.swing.Timers or "Swing Timers".这是一个 Swing 应用程序,您不应使用 java.util.Timers,而应使用 javax.swing.Timers 或“Swing Timers”。 The reason for this is that Swing Timers work well with the Swing event thread while Utility Timers do not, and this can lead to occurrence of intermittent and devilishly hard to debug errors.这样做的原因是 Swing 计时器与 Swing 事件线程一起工作得很好,而实用程序计时器则不能,这可能会导致出现间歇性且极其难以调试的错误。
  • You shouldn't be using a while loop as you're doing as this risks blocking the Swing event thread (or EDT for "Event Dispatch Thread"), rendering your GUI frozen.您不应该在执行时使用 while 循环,因为这可能会阻塞 Swing 事件线程(或“事件调度线程”的 EDT),从而导致 GUI 冻结。 Instead use your Timer as well as a State Machine design to prevent need for the while loop.而是使用您的计时器以及状态机设计来防止需要 while 循环。

If you need more detailed help, then please respond to comments above, and help us with your minimal example program , one we can run and modify ourselves.如果您需要更详细的帮助,请回复上面的评论,并帮助我们提供您的最小示例程序,一个我们可以自己运行和修改的程序。 We don't want to see your entire program, nor do we want links, but rather the smallest program that shows us your problem.我们不想看到您的整个程序,也不想看到链接,而是希望看到向我们展示您的问题的最小程序。

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

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