Multiple Swing Timers Cause Multiple Actions to Happen

前端 未结 1 1037
清歌不尽
清歌不尽 2021-01-26 12:17

I\'m doing a Java project where I have to make a GUI that contains multiple timers that countdown from a user input time. When I create only one timer, the program works fine, b

相关标签:
1条回答
  • 2021-01-26 12:39

    The problem is with your TimerListener.

    Basically, what's happening, is each time it is called, it executes this code block...

    if (go[i] == true && !cdt[i].equals(zero)) {
        javaTimer[i].start();
        cdt[i].dec();
        timeDisplay[i].setText(cdt[i].toString());
        count[i]++;
    }
    

    This means, if you have 3 counters running, this code block will be executed 9 times per second. Three times per counter/Timer

    You TimerListener is too generalised and is trying to do too many things.

    Either, create a "single" Timer that ticks on a regular bases and updates each counter as required (so that each loop is dealing with a single counter) OR modify your TimerListener so that it takes details about the counter it is managing and deals ONLY with that counter.

    Updated with possible solution

    This example uses a single, centralised Timer which simply ticks away in the background. As required, a special Counter is registered with the Timer, which then notifies it when a tick occurs. The Counter then decrements the CountDownTimer and updates the UI.

    This is an imperfect example, as I would prefer that the CountDownTimer had a listener interface capable of notifying registered listeners that object's state has changed in some way. This could be achieved through the wrapper Counter class, but I'm to lazy.

    Timer

    import java.awt.EventQueue;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Scanner;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class TestTimerPane {
    
        public static void main(String[] args) {
            new TestTimerPane();
        }
    
        public TestTimerPane() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    CountDownManager countDownManager = new CountDownManager();
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new GridBagLayout());
                    GridBagConstraints gbc = new GridBagConstraints();
                    gbc.gridwidth = GridBagConstraints.REMAINDER;
                    frame.add(new TimerPane(countDownManager), gbc);
                    frame.add(new TimerPane(countDownManager), gbc);
                    frame.add(new TimerPane(countDownManager), gbc);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class CountDownManager {
    
            private List<Counter> timers;
    
            public CountDownManager() {
                timers = new ArrayList<>(25);
    
                Timer timer = new Timer(1000, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        Iterator<Counter> it = timers.iterator();
                        while (it.hasNext()) {
                            it.next().tick(CountDownManager.this);
                        }
                    }
                });
                timer.start();
            }
    
            public void add(Counter timer) {
                timers.add(timer);
            }
    
            public void remove(Counter timer) {
                timers.remove(timer);
            }
    
        }
    
        public class TimerPane extends JPanel {
    
            private JTextField fldHours;
            private JTextField fldMins;
            private JTextField fldSecs;
    
            private JButton btnStart;
            private JButton btnStop;
            private JButton btnSet;
    
            private JLabel countDown;
    
            private CountDownTimer countDownTimer;
    
            private Counter counter;
    
            public TimerPane(final CountDownManager countDownManager) {
                fldHours = new JTextField(2);
                fldMins = new JTextField(2);
                fldSecs = new JTextField(2);
    
                btnSet = new JButton("Set");
                btnStop = new JButton("Stop");
                btnStart = new JButton("Start");
    
                countDown = new JLabel("??:??:??");
    
                add(fldHours);
                add(new JLabel(":"));
                add(fldMins);
                add(new JLabel(":"));
                add(fldSecs);
                add(btnSet);
                add(btnStart);
                add(btnStop);
                add(countDown);
    
                btnSet.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        countDownTimer = new CountDownTimer(
                                toInt(fldHours),
                                toInt(fldMins),
                                toInt(fldSecs));
                        counter = new Counter(countDown, countDownTimer);
                        countDown.setText(countDownTimer.toString());
                    }
                });
    
                btnStart.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (counter != null) {
                            countDownManager.add(counter);
                        }
                    }
                });
                btnStop.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (counter != null) {
                            countDownManager.remove(counter);
                        }
                    }
                });
            }
    
            protected int toInt(JTextField field) {
                int value = 0;
                try {
                    value = Integer.parseInt(field.getText());
                } catch (NumberFormatException exp) {
                }
                return value;
            }
    
        }
    
        public class Counter {
    
            private JLabel output;
            private CountDownTimer timer;
    
            public Counter(JLabel output, CountDownTimer timer) {
                this.output = output;
                this.timer = timer;
            }
    
            public void tick(CountDownManager manager) {
                timer.dec();
                output.setText(timer.toString());
                if (timer.convertToSeconds() <= 0) {
                    manager.remove(this);
                }
            }
    
        }
    
        public static class CountDownTimer {
    
            /**
             * Amount of hours that are being counted down in the timer
             */
            int hours;
            /**
             * Amount of minutes that are being counted down in the timer
             */
            int minutes;
            /**
             * Amount of seconds that are being counted down in the timer
             */
            int seconds;
    
            /**
             * Boolean that determines if the add method will work. It is changed with
             * the toggleSuspend method
             */
            static boolean toggle = false;
    
            /**
             * ***************************************************************************************
             * Default constructor that creates a CountDownTimer object with no time
             * contained in it
             * ***************************************************************************************
             */
            public CountDownTimer() {
                hours = 0;
                minutes = 0;
                seconds = 0;
            }
    
            /**
             * *******************************************************************************************
             * Constructor that uses the input amount of hours, minutes, and seconds to
             * count down from
             *
             * Does not allow time that is negative or allow seconds or minutes that are
             * over 60
             *
             * @param hours amount of hours that will be counted down from
             * @param minutes amount of minutes that will be counted down from
             * @param seconds amount of seconds that will be counted down from
             * *******************************************************************************************
             */
            public CountDownTimer(int hours, int minutes, int seconds) {
                super();
    
                // doesn't allow negative minutes, seconds, or hours
                if (seconds < 0 || minutes < 0 || hours < 0) {
                    throw new IllegalArgumentException("Time cannot be negative");
                }
                // doesn't allow seconds that are higher than 60
                if (seconds >= 60) {
                    throw new IllegalArgumentException(
                            "Cannot have more than 60 seconds");
                }
                // doesn't allow minutes that are higher than 60
                if (minutes >= 60) {
                    throw new IllegalArgumentException(
                            "Cannot have more than 60 minutes");
                }
                this.hours = hours;
                this.minutes = minutes;
                this.seconds = seconds;
            }
    
            /**
             * *******************************************************************
             * Constructor that takes minutes and seconds, and sets hours to zero also
             * doesn't allow minutes or seconds to be negative or above 60
             *
             * @param minutes amount of minutes that will be counted down from
             * @param seconds amount of seconds that will be counted down from
             * ***********************************************************************
             */
            public CountDownTimer(int minutes, int seconds) {
                super();
                // doesn't allow seconds minutes or hours to be negative
                if (seconds < 0 || minutes < 0 || hours < 0) {
                    throw new IllegalArgumentException("Time cannot be negative");
                }
                // doesn't allow seconds to be greater than 60
                if (seconds >= 60) {
                    throw new IllegalArgumentException(
                            "Cannot have more than 60 seconds");
                }
                // doesn't allow minutes to be greater than 60
                if (minutes >= 60) {
                    throw new IllegalArgumentException(
                            "Cannot have more than 60 minutes");
                }
    
                this.hours = 0;
                this.minutes = minutes;
                this.seconds = seconds;
            }
    
            /**
             * *********************************************************************
             * Constructor that only takes seconds and sets hours and minutes to 0 does
             * not allow the seconds to be above 60 or negative
             *
             * @param seconds amount of seconds that will be counted down from
             * ***********************************************************************
             */
            public CountDownTimer(int seconds) {
                super();
    
                // doesn't allow seconds minutes or hours to be negative
                if (seconds < 0 || minutes < 0 || hours < 0) {
                    throw new IllegalArgumentException("Time cannot be negative");
                }
                // doesn't allow seconds to be greater than 60
                if (seconds >= 60) {
                    throw new IllegalArgumentException(
                            "Cannot have more than 60 seconds");
                }
                this.hours = 0;
                this.minutes = 0;
                this.seconds = seconds;
            }
    
            /**
             * Constructor that clones one CountDownTimer object into a new
             * CountDownTimer object
             *
             * @param other The CountDownTimer object that is being cloned
             *
             */
            public CountDownTimer(CountDownTimer other) {
                this.hours = other.hours;
                this.minutes = other.minutes;
                this.seconds = other.seconds;
            }
    
            /**
             * *****************************************************************************************************************************
             * Constructor that converts a string in the format of "00:00:00" into
             * seconds minutes and hours so it can be counted down from
             *
             * @param startTime String that is converted into seconds minutes and hours
             * *****************************************************************************************************************************
             */
            public CountDownTimer(String startTime) {
                // Separates the seconds minutes and hours into an array
                String[] parts = startTime.split(":");
                // if the array has only one cell, that means only seconds were input
                if (parts.length == 1) {
                    seconds = Integer.parseInt(parts[0]);
                }
                // if the array has only 2 cells that means there is only minutes and seconds input
                if (parts.length == 2) {
                    minutes = Integer.parseInt(parts[0]);
                    seconds = Integer.parseInt(parts[1]);
                }
                // if the array has 3 cells that means there is seconds minutes and hours input
                if (parts.length == 3) {
                    hours = Integer.parseInt(parts[0]);
                    minutes = Integer.parseInt(parts[1]);
                    seconds = Integer.parseInt(parts[2]);
                }
                // doesn't allow seconds minutes or hours to be negative
                if (seconds < 0 || minutes < 0 || hours < 0) {
                    throw new IllegalArgumentException("Time cannot be negative");
                }
                // doesn't allow seconds to be greater than or equal to 60
                if (seconds >= 60) {
                    throw new IllegalArgumentException(
                            "Cannot have more than 60 seconds");
                }
                // doesn't allow minutes to be greater than or equal to 60
                if (minutes >= 60) {
                    throw new IllegalArgumentException(
                            "Cannot have more than 60 minutes");
                }
            }
    
            /**
             * ************************************************************************************************
             * Method that returns true or false based on whether or not two
             * CountDownTimer objects are equal
             *
             * @param other Object that is being compared to another CountDownTimer
             * ************************************************************************************************
             */
            public boolean equals(Object other) {
                // converts the two objects to seconds then compares them
                if (this.convertToSeconds() == ((CountDownTimer) other)
                        .convertToSeconds()) {
                    return true;
                }
                return false;
            }
    
            /**
             * ******************************************************************************
             * Returns a boolean based on whether two CountDownTimers, t1 and t2, are
             * equal
             *
             * @param t1 first CountDownTimer being compared
             * @param t2 second CountDownTimer being compared
             * ******************************************************************************
             */
            public static boolean equals(CountDownTimer t1, CountDownTimer t2) {
                // converts the two objects to seconds and then compares them
                if (t1.convertToSeconds() == t2.convertToSeconds()) {
                    return true;
                }
                return false;
            }
    
            /**
             * **********************************************************************************************
             * Compares to CountDownTimer objects and returns an int 1, 0, or -1 based
             * on whether the first object is greater than, equal to, or less than the
             * CountDownTimer in the parameter
             *
             * @param other CountDownTimer that is being compared
             * ***********************************************************************************************
             */
            public int compareTo(CountDownTimer other) {
                if (this.convertToSeconds() > other.convertToSeconds()) {
                    return 1;
                } else if (this.convertToSeconds() < other.convertToSeconds()) {
                    return -1;
                }
                return 0;
            }
    
            /**
             * ************************************************************************************************
             * Compares to CountDownTimer objects and returns an int 1, 0, or -1 based
             * on whether the first object (t1) is greater than, equal to, or less than
             * the second object (t2)
             *
             * @param t1 first object being compared
             * @param t2 second object being compared
             * @return
             * *************************************************************************************************
             */
            public static int compareTo(CountDownTimer t1, CountDownTimer t2) {
                if (t1.convertToSeconds() > t2.convertToSeconds()) {
                    return 1;
                } else if (t1.convertToSeconds() < t2.convertToSeconds()) {
                    return -1;
                }
                return 0;
            }
    
            /**
             * *************************************************************
             * subtracts the input amount of seconds from a CountDownTimer
             *
             * @param seconds amount of seconds the user wants to subtract
             * *************************************************************
             */
            public void subtract(int seconds) {
                // places the amount of seconds into an integer
                int tempSeconds = this.convertToSeconds();
                // subtracts the input seconds from the seconds that were converted
                tempSeconds -= seconds;
                // converts the new seconds back into the object
                formatSeconds(tempSeconds);
            }
    
            /**
             * *****************************************************************************************
             * Subtracts the amount of time contained in one CountDownTimer from another
             * CountDownTimer
             *
             * @param other CountDownTimer that is doing the subtracting
             * *****************************************************************************************
             */
            public void subtract(CountDownTimer other) {
                int otherSeconds = other.convertToSeconds();
                this.subtract(otherSeconds);
            }
    
            /**
             * *********************************************************************
             * Adds seconds to the object based on what is put into the parameter
             *
             * @param seconds amount of seconds being added to the CountDownTimer
             * **********************************************************************
             */
            public void add(int seconds) {
                // keeps the method from adding when the toggle is activated
                if (toggle == false) {
                    int tempSeconds = this.convertToSeconds();
                    tempSeconds += seconds;
                    formatSeconds(tempSeconds);
                } else {
                    throw new IllegalArgumentException(
                            "Cannot use add when toggle is enabled");
                }
            }
    
            /**
             * Adds the seconds from one CountDownTimer to another CountDownTimer
             *
             * @param other CountDownTimer that is being added to another CountDowntimer
             */
            public void add(CountDownTimer other) {
                // doesn't allow the method to add when the toggle is true
                if (toggle == false) {
                    int otherSeconds = other.convertToSeconds();
                    this.add(otherSeconds);
                } else {
                    throw new IllegalArgumentException(
                            "Cannot use add when toggle is enabled");
                }
            }
    
            /**
             * *****************************************
             * Decreases the CountDownTimer by 1 second
             * *****************************************
             */
            public void dec() {
                int tempSeconds = this.convertToSeconds();
                tempSeconds--;
                formatSeconds(tempSeconds);
            }
    
            /**
             * **************************************************
             * Increases the CountDownTimer object by 1 second
             * *************************************************
             */
            public void inc() {
                int tempSeconds = this.convertToSeconds();
                tempSeconds--;
                formatSeconds(tempSeconds);
            }
    
            /**
             * Returns the object as a string in the format of "00:00:00"
             */
            public String toString() {
                String time = "" + this.hours + ":";
                if (this.minutes < 10) {
                    time += "0" + this.minutes + ":";
                } else {
                    time += this.minutes + ":";
                }
                if (this.seconds < 10) {
                    time += "0" + this.seconds;
                } else {
                    time += this.seconds;
                }
                return time;
            }
    
            /**
             * **********************************************
             * Saves the object with a specified name
             *
             * @param fileName name of the file being saved
             * ***********************************************
             */
            public void save(String fileName) {
                PrintWriter out = null;
                try {
                    out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
                } catch (IOException e) {
                    e.printStackTrace();
                }
                out.println(this.hours);
                out.println(this.minutes);
                out.println(this.seconds);
                out.close();
            }
    
            /**
             * ************************************************
             * Loads the object with the specified name
             *
             * @param fileName Name of the file being loaded
             * ************************************************
             */
            public void load(String fileName) {
                try {
                    Scanner fileReader = new Scanner(new File(fileName));
    
                    this.hours = fileReader.nextInt();
                    this.minutes = fileReader.nextInt();
                    this.seconds = fileReader.nextInt();
    
                    fileReader.close();
                    System.out.println("Hours: " + this.hours);
                    System.out.println("Minutes: " + this.minutes);
                    System.out.println("Seconds: " + this.seconds);
                } catch (FileNotFoundException error) {
                    System.out.println("File not found");
                } catch (IOException error) {
                    System.out.println("OH NO THAT WAS NOT SUPPOSED TO HAPPEN");
                }
            }
    
            /**
             * ********************************************************************************************
             * Switches the toggle boolean, and doesn't allow the add methods to work
             * when it is activated
             * **********************************************************************************************
             */
            public static void toggleSuspend() {
                if (toggle == false) {
                    toggle = true;
                }
                if (toggle == true) {
                    toggle = false;
                }
            }
    
            /**
             * *********************************************************************************
             * Formats a certain amount of seconds and puts it into an existing
             * CountDownTimer
             *
             * @param seconds seconds being formatted
             * *********************************************************************************
             */
            private void formatSeconds(int seconds) {
                this.hours = seconds / 3600;
                seconds %= 3600;
                this.minutes = seconds / 60;
                this.seconds = seconds % 60;
            }
    
            /**
             * ***************************************************************************
             * Returns the amount of seconds that are contained in a CountDownTime
             * object
             * ***************************************************************************
             */
            private int convertToSeconds() {
                int hSeconds = hours * 3600;
                int mSeconds = minutes * 60;
                return hSeconds + mSeconds + seconds;
            }
        }
    
    }
    

    Now, if you need a separate Timer per CountDownTimer, then I would probably create some kind of "worker" class that would take a CountDownTimer and that had a self contained Timer. I would also provide some kind of listener that interested parties could register to and would allow them to update the UI as they see fit...

    0 讨论(0)
提交回复
热议问题