Пауза и возобновление для Swing Timer не работает должным образом - PullRequest
0 голосов
/ 25 октября 2018

Моя программа - игра на память.При каждом нажатии на плитку таймер запускается.Я пытаюсь создать меню для паузы и возобновления таймера 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();
}

1 Ответ

0 голосов
/ 25 октября 2018

Итак, вы фиксируете 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 никогда не сбрасывался, поэтому он по-прежнему рассчитывается с момента, когда он был инициализирован.

Концептуально, часы с "паузой" - это часы, которые имеютдва важных состояния.

  • «Текущее» время выполнения.Это время с момента его запуска / возобновления до настоящего времени
  • «Предыдущее» время выполнения.Это общее количество времени, в течение которого таймеру разрешено работать.

Итак.Когда вы останавливаете часы, вам нужно сделать пару вещей ...

  1. Рассчитать общее время работы для этой фазы - время с момента его запуска до настоящего времени.
  2. Добавьте это к общему «времени работы», которое отслеживает общее время, в течение которого часам было разрешено запускать.
  3. Сброс времени запуска (предпочтительно примерно на null, так что это очевидно)

При возобновлении необходимо ...

  1. Сохранить текущее время как start.
  2. Рассчитать время между start время и сейчас (когда тикают часы)
  3. Добавьте 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;
    }
}

И если вы хотите увидеть концепцию в действии, вы можетевзгляните на Добавление функции возобновления к секундомеру

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...