Моя программа - игра на память. Каждый раз, когда щелкают плитку, запускается таймер. Я пытаюсь создать меню для паузы и возобновления работы таймера Swing. Пауза работает нормально; однако проблема, с которой я сталкиваюсь, заключается в том, что всякий раз, когда я возобновляю таймер после паузы, он пропускает четыре секунды вместо продолжения таймера. Конкретные методы для таймера размещаются под всем программным кодом. Вот что у меня сейчас есть:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.*;
import java.util.Collections;
import java.util.Calendar;
import java.time.*;
public class MemoryGame implements ActionListener {
private Timer cdTimer; //Count down timer of 1.5 secs for unmatched pairs
private Timer swTimer; //Main timer for the game
private int count = 0;
private long start = Calendar.getInstance().getTimeInMillis();
private long now;
private long elapsed;
boolean match = false; //Determine if the cdTimer is running or not
private JToggleButton[] buttons;
private JToggleButton first; //Variable for the first button to match
private JLabel time; //Label to hold the
private JMenuItem pause;
private JMenuItem resume;
ArrayList<ImageIcon> iconList = new ArrayList();
ArrayList<JToggleButton> retireButton = new ArrayList();
ImageIcon icon = new ImageIcon("MemoryGame.png");
public MemoryGame() {
JFrame jfrm = new JFrame("Memory Game");
jfrm.setSize(1000, 1000);
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jfrm.setIconImage(icon.getImage());
time = new JLabel("Elapsed time is 00:00:00");
GridLayout layout = new GridLayout(3,4);
JPanel gamePanel = new JPanel();
gamePanel.setLayout(layout);
createIcons(); //set icons for the tiles
buttons = new JToggleButton[12];
for(int i = 0; i < buttons.length; i++) {
JToggleButton btn = new JToggleButton(icon);
buttons[i] = btn;
buttons[i].addActionListener(this);
gamePanel.add(buttons[i]);
}
Collections.shuffle(Arrays.asList(buttons));
Collections.shuffle(iconList);
jfrm.add(gamePanel, BorderLayout.CENTER);
time.setHorizontalAlignment(JLabel.CENTER);
time.setVerticalAlignment(JLabel.CENTER);
jfrm.add(time, BorderLayout.NORTH);
//Create menus
JMenuBar jm = new JMenuBar();
JMenu action = new JMenu("Action");
action.setMnemonic(KeyEvent.VK_A);
JMenu gameTimer = new JMenu("Game Timer");
gameTimer.setMnemonic(KeyEvent.VK_T);
pause = new JMenuItem("Pause", KeyEvent.VK_P);
pause.setAccelerator(KeyStroke.getKeyStroke("control P"));
pause.setEnabled(false);
pause.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
elapsed += now;
swTimer.stop();
}
});
resume = new JMenuItem("Resume", KeyEvent.VK_R);
resume.setAccelerator(KeyStroke.getKeyStroke("control R"));
resume.setEnabled(false);
resume.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
timerContinue();
}
});
gameTimer.add(pause);
gameTimer.add(resume);
action.add(gameTimer);
JMenuItem reveal = new JMenuItem("Reveal", KeyEvent.VK_R);
reveal.addActionListener(this);
action.add(reveal);
action.addSeparator();
JMenuItem exit = new JMenuItem("Exit", KeyEvent.VK_X);
exit.addActionListener(this);
action.add(exit);
JMenu help = new JMenu("Help");
help.setMnemonic(KeyEvent.VK_H);
JMenuItem viewHelp = new JMenuItem("View Help...",
KeyEvent.VK_H);
viewHelp.addActionListener(this);
help.add(viewHelp);
help.addSeparator();
JMenuItem about = new JMenuItem("About", KeyEvent.VK_A);
about.addActionListener(this);
help.add(about);
jm.add(action);
jm.add(help);
jfrm.setJMenuBar(jm);
jfrm.setLocationRelativeTo(null);
jfrm.setVisible(true);
}
public void actionPerformed(ActionEvent e){
//this if makes sure the timer does not restart everytime a button is clicked
if (retireButton.size() == 0){
timerStart();
}
if (swTimer.isRunning()){
pause.setEnabled(true);
resume.setEnabled(true);
}
//this if makes sure no button will be input during the 1.5 secs delay
if (match == false){
JToggleButton btn = (JToggleButton)e.getSource(); //take in button
setIcon(btn);
resetIcon(btn);
//this if makes btn equals the first button if it is null
if (first == null){
first = btn;
return;
}
matching(first, btn);
first = null;
}
}
public void updateTime(){
long temp = Calendar.getInstance().getTimeInMillis();
time.setText("Elapsed time is " + formatTime((long) (temp - start)));
now = temp - start;
}
public void continueTime(){
long temp = Calendar.getInstance().getTimeInMillis();
time.setText("Elapsed time is " + formatTime((long) (temp - start)));
}
private void timerContinue(){
ActionListener timerAL = new ActionListener(){
public void actionPerformed(ActionEvent e){
continueTime();
}
};
//stop the timer if it is still running
if (swTimer != null && swTimer.isRunning()) {
swTimer.stop();
}
swTimer = new Timer(1000, timerAL);
swTimer.setInitialDelay(0);
swTimer.start();
}
public static String formatTime(long ms){
long millis = ms % 1000;
long x = ms / 1000;
long seconds = x % 60;
x /= 60;
long minutes = x % 60;
x /= 60;
long hours = x % 24;
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}
//Method to reset the button to game image when it is clicked for a second time
private void resetIcon(JToggleButton btn){
if (!btn.isSelected()){
btn.setIcon(icon);
}
}
private void timerStart(){
ActionListener timerAL = new ActionListener(){
public void actionPerformed(ActionEvent e){
updateTime();
}
};
//stop the timer if it is still running
if (swTimer != null && swTimer.isRunning()) {
swTimer.stop();
}
swTimer = new Timer(1000, timerAL);
swTimer.setInitialDelay(0);
swTimer.start();
}
private void timerStop(){
//if all 12 buttons are matched, then stop the timer
if (retireButton.size() == 12){
long stop = Calendar.getInstance().getTimeInMillis();
time.setText("You finished in " + formatTime((long)(stop-start)));
swTimer.stop();
}
}
//set the icons for the tiles
private void setIcon(JToggleButton btn) {
if (btn == buttons[0] || btn == buttons[1])
btn.setIcon(iconList.get(0));
else if (btn == buttons[2] || btn == buttons[3])
btn.setIcon(iconList.get(1));
else if (btn == buttons[4] || btn == buttons[5])
btn.setIcon(iconList.get(2));
else if (btn == buttons[6] || btn == buttons[7])
btn.setIcon(iconList.get(3));
else if (btn == buttons[8] || btn == buttons[9])
btn.setIcon(iconList.get(4));
else if (btn == buttons[10] || btn == buttons[11])
btn.setIcon(iconList.get(5));
}
//match the two input buttons
private void matching(JToggleButton btn, JToggleButton btn2){
if (btn.isSelected()){
if (btn2.isSelected()){
buttonDisable(btn, btn2); //disable all buttons besides btn, and btn2
if (!btn.getIcon().toString().equals(btn2.getIcon().toString())){
startTime(1, btn, btn2); //start the 1.5 secs countdown
}
else {
retirePair(btn, btn2);
timerStop();
buttonEnable(btn, btn2);
}
}
}
}
private void startTime(int countPassed, JToggleButton btn, JToggleButton btn2){
ActionListener action = new ActionListener(){
public void actionPerformed(ActionEvent e){
if (count == 0){
cdTimer.stop();
match = false; //resets match
unflipPair(btn, btn2); //reset tile to game image again
buttonEnable(btn, btn2);
}
else
count--;
}
};
cdTimer = new Timer(500, action);
cdTimer.start();
match = true;
count = countPassed;
}
//enable buttons other than btn and btn2
private void buttonEnable(JToggleButton btn, JToggleButton btn2){
for(int i = 0; i < buttons.length; i++){
if (buttons[i] != btn && buttons[i] != btn2)
buttons[i].setEnabled(true);
}
}
//disable buttons other than btn and btn2
private void buttonDisable(JToggleButton btn, JToggleButton btn2){
for(int i = 0; i < buttons.length; i++){
if (buttons[i] != btn && buttons[i] != btn2)
buttons[i].setEnabled(false);
}
}
private void unflipPair(JToggleButton btn, JToggleButton btn2){
btn.setIcon(icon);
btn2.setIcon(icon);
btn.setEnabled(true);
btn2.setEnabled(true);
btn.setSelected(false);
btn2.setSelected(false);
}
private void retirePair(JToggleButton btn, JToggleButton btn2){
btn.setSelected(true);
btn2.setSelected(true);
btn.removeActionListener(this);
btn2.removeActionListener(this);
retireButton.add(btn);
retireButton.add(btn2);
}
private void createIcons(){
ImageIcon icon1 = new ImageIcon("1.png");
ImageIcon icon2 = new ImageIcon("2.png");
ImageIcon icon3 = new ImageIcon("3.png");
ImageIcon icon4 = new ImageIcon("4.png");
ImageIcon icon5 = new ImageIcon("5.png");
ImageIcon icon6 = new ImageIcon("6.png");
iconList.add(icon1);
iconList.add(icon2);
iconList.add(icon3);
iconList.add(icon4);
iconList.add(icon5);
iconList.add(icon6);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (args.length == 0) //java MemoryGame debug increases the string length to 1
new MemoryGame();
else
new MemoryGame(2);
}
});
}
}
Это addActionListener:
pause.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
elapsed += now;
swTimer.stop();
}
});
resume.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
timerContinue();
}
});
Это метод timerContinue:
public void continueTime(){
long temp = Calendar.getInstance().getTimeInMillis();
time.setText("Elapsed time is " + formatTime((long) (temp - start)));
}
private void timerContinue(){
ActionListener timerAL = new ActionListener(){
public void actionPerformed(ActionEvent e){
continueTime();
}
};
//stop the timer if it is still running
if (swTimer != null && swTimer.isRunning()) {
swTimer.stop();
}
swTimer = new Timer(1000, timerAL);
swTimer.setInitialDelay(0);
swTimer.start();
}
Это метод timeStart ():
public void updateTime(){
long temp = Calendar.getInstance().getTimeInMillis();
time.setText("Elapsed time is " + formatTime((long) (temp - start)));
now = temp - start;
}
private void timerStart(){
ActionListener timerAL = new ActionListener(){
public void actionPerformed(ActionEvent e){
updateTime();
}
};
//stop the timer if it is still running
if (swTimer != null && swTimer.isRunning()) {
swTimer.stop();
}
swTimer = new Timer(1000, timerAL);
swTimer.setInitialDelay(0);
swTimer.start();
}
Итак, вы захватили start
private long start = Calendar.getInstance().getTimeInMillis();
когда класс создается впервые ... не уверен, что это хорошая идея, но давайте продолжим ...
Когда вы ставите таймер на паузу ...
pause.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
elapsed += now;
swTimer.stop();
}
});
вы захватываете время elapsed
Когда вы возобновите часы ...
public void continueTime(){
long temp = Calendar.getInstance().getTimeInMillis();
time.setText("Elapsed time is " + formatTime((long) (temp - start)));
}
private void timerContinue(){
ActionListener timerAL = new ActionListener(){
public void actionPerformed(ActionEvent e){
continueTime();
}
};
//stop the timer if it is still running
if (swTimer != null && swTimer.isRunning()) {
swTimer.stop();
}
swTimer = new Timer(1000, timerAL);
swTimer.setInitialDelay(0);
swTimer.start();
}
Вы используете текущее время и вычитаете start
, но start
никогда не сбрасывался, поэтому он все еще вычисляется с момента первой инициализации.
Концептуально часы с возможностью паузы - это часы, которые имеют два важных состояния.
Так. Когда вы ставите часы на паузу, вам нужно сделать пару вещей ...
null
, чтобы было очевидно)При возобновлении нужно ...
start
.start
и настоящим моментом (когда часы тикают)totalRunningTime
, который определяет общее время работы часов.Просто ?
Во-первых, сам вопрос не так уж редок, и есть несколько решений, если вы потратите некоторое время на изучение проблемы.
Сказав это, поскольку сейчас 2018 год и Java 11 видна на горизонте, вам действительно следует использовать новый API даты / времени Java, который предоставляет ряд действительно полезных API, включая Duration
и возможность обрабатывать время независимо от лежащая в основе календарная система ... так что мы не получаем странностей с переходом на летнее время ?
Ниже приведен пример некоторого библиотечного кода, который я использую, который реализует указанные выше функции.
"Странная" вещь в том, что это не зависит от системы отсчета времени. То есть не «тикает». Вместо этого вы должны использовать Swing Timer
(в вашем случае) для вызова getDuration
и обновления пользовательского интерфейса по мере необходимости.
public class StopWatch {
private Instant startTime;
private Duration totalRunTime = Duration.ZERO;
public StopWatch start() {
startTime = Instant.now();
return this;
}
public StopWatch stop() {
Duration runTime = Duration.between(startTime, Instant.now());
totalRunTime = totalRunTime.plus(runTime);
startTime = null;
return this;
}
public StopWatch pause() {
return stop();
}
public StopWatch resume() {
return start();
}
public StopWatch reset() {
stop();
totalRunTime = Duration.ZERO;
return this;
}
public boolean isRunning() {
return startTime != null;
}
public Duration getDuration() {
Duration currentDuration = Duration.ZERO;
currentDuration = currentDuration.plus(totalRunTime);
if (isRunning()) {
Duration runTime = Duration.between(startTime, LocalDateTime.now());
currentDuration = currentDuration.plus(runTime);
}
return currentDuration;
}
}
И если вы хотите увидеть концепцию в действии, вы можете взглянуть на Добавление функции возобновления в секундомер.
Большое спасибо за объяснение концепции. Я смог это реализовать.
start
никогда не изменяется, несмотря на то, что вы рассчитываете времяelasped
, вы никогда не применяете его в своих расчетах.