Stopwatch unpause doesn't work

半腔热情 提交于 2020-01-06 21:23:03

问题


I'm trying to create a stopwatch. The start and pause buttons works fine but the unpause button doesn't work properly. timer is my JLabel in which I want to ilustrate my stopwatch (it references timer from the JFrame). I can't post a MCVE because it's too much code.

Here is my Stopwatch class:

public class Stopwatch extends Thread {

    private boolean finishedFlag = false;
    private boolean pauseFlag = false;
    private boolean sortFlag = false;
    private long summedTime = 0;
    private JLabel timer;

    public Stopwatch(){}

    public Stopwatch(boolean finished, boolean pause, boolean sort, JLabel timer){
        this.finishedFlag = finished;
        this.pauseFlag = pause;
        this.sortFlag = sort;
        this.timer = timer;
    }

    @Override
    public void run() {
        long startTime = System.currentTimeMillis();
        while(sortFlag && !pauseFlag && !finishedFlag) {
            update(summedTime + (System.currentTimeMillis() - startTime));
        }
        if(pauseFlag)
            summedTime += System.currentTimeMillis() - startTime;
        else 
            summedTime = 0;
    }

    private void update(long dT){
        long x = (dT/1000)%60;
        long y = (dT/60000)%1000;
        if(x>=0 && x<=9 && y>=0 && y<=9)
            timer.setText("0"+String.valueOf((dT/60000)%1000)+":0"+String.valueOf((dT/1000)%60)+","+String.valueOf((dT)%1000));
        else if(x>9 && y>=0 && y<=9)
            timer.setText("0"+String.valueOf((dT/60000)%1000)+":"+String.valueOf((dT/1000)%60)+","+String.valueOf((dT)%1000));
        else if(x>=0 && x<=9 && y>9)
            timer.setText(String.valueOf((dT/60000)%1000)+":0"+String.valueOf((dT/1000)%60)+","+String.valueOf((dT)%1000));
        else if(x>9 && y>9)
            timer.setText(String.valueOf((dT/60000)%1000)+":"+String.valueOf((dT/1000)%60)+","+String.valueOf((dT)%1000));
    }
}

This are the listeners I use for my buttons:

ActionListener sortListener = new ActionListener(){

        @Override
        public void actionPerformed(ActionEvent e) {
            if(sortFlag == false && pauseFlag == false)
            {
                sortFlag = true;
                System.out.println("sort");
                stopwatch1 = new Stopwatch(finishedFlag,pauseFlag,sortFlag,timer1);
                stopwatch1.start();
                appFrame.validate();
                appFrame.repaint();
            }
        }
    };
    sortButton.addActionListener(sortListener);

    ActionListener pauseListener = new ActionListener(){

        @Override
        public void actionPerformed(ActionEvent e) {
            if(sortFlag && pauseFlag == false)
            {
                pauseFlag = true;
                timeSpent = stopwatch1.getSummedTime();
                stopwatch1.setPauseFlag(true);
                System.out.println("pause");
            }
        }
    };
    pauseButton.addActionListener(pauseListener);

    ActionListener unpauseListener = new ActionListener(){

        @Override
        public void actionPerformed(ActionEvent e) {
            if(sortFlag && pauseFlag)
            {
                pauseFlag = false;
                stopwatch1.setPauseFlag(false);
                stopwatch1.run();
                System.out.println("unpause");
            }
        }
    };
    unpauseButton.addActionListener(unpauseListener);

回答1:


You need someway to pause the Thread and stop the update from occurring. You could do this with an if statement inside your run loop, but there is a more efficient way through the use of monitor lock

public class Stopwatch extends Thread {
    //...
    private final Object pauseLock;

    public Stopwatch() {
        pauseLock = new Object();
    }

    public Stopwatch(boolean finished, boolean pause, boolean sort, JLabel timer) {
        this();
        //...
    }

    @Override
    public void run() {
        long startTime = System.currentTimeMillis();
        while (sortFlag && !finishedFlag) {
            while (pauseFlag) {
                synchronized (pauseLock) {
                    try {
                        pauseLock.wait();
                    } catch (InterruptedException ex) {
                    }
                }
            }
            update(summedTime + (System.currentTimeMillis() - startTime));
        }
        if (pauseFlag) {
            summedTime += System.currentTimeMillis() - startTime;
        } else {
            summedTime = 0;
        }
    }

Now, you need some way to pause and resume the run loop

public void setPaused(boolean paused) {
    if (paused && !pauseFlag) {
        pauseFlag = paused;
    } else if (!paused && pauseFlag) {
        pauseFlag = paused;
        synchronized (pauseLock) {
            pauseLock.notifyAll();
        }
    }
}

Now, however, we have bigger problem, Swing is NOT thread safe. This means that your update method is breaking the single thread rules of Swing and could cause no end of issues...

A simpler solution would be to just use a Swing Timer

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JLabel label;
        private StopWatch sw;

        public TestPane() {

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            label = new JLabel("...");
            add(label, gbc);

            sw = new StopWatch(label);

            JButton btn = new JButton("Resume");
            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    sw.setPaused(!sw.isPaused());
                    btn.setText(sw.isPaused() ? "Resume" : "Pause");
                }
            });
            add(btn, gbc);
        }

    }

    public class StopWatch {

        private Timer timer;
        private JLabel label;

        private int runningTime;
        private long tickTime;

        public StopWatch(JLabel label) {
            this.label = label;
            timer = new Timer(10, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    runningTime += (System.currentTimeMillis() - tickTime);
                    System.out.println(runningTime);
                    update(runningTime);
                    tickTime = System.currentTimeMillis();
                }
            });
        }

        public void setPaused(boolean paused) {
            if (paused && timer.isRunning()) {
                timer.stop();
            } else if (!paused && !timer.isRunning()) {
                tickTime = System.currentTimeMillis();
                timer.start();
            }
        }

        public boolean isPaused()  {
            return !timer.isRunning();
        }

        private void update(long dT) {
            long x = (dT / 1000) % 60;
            long y = (dT / 60000) % 1000;
            if (x >= 0 && x <= 9 && y >= 0 && y <= 9) {
                label.setText("0" + String.valueOf((dT / 60000) % 1000) + ":0" + String.valueOf((dT / 1000) % 60) + "," + String.valueOf((dT) % 1000));
            } else if (x > 9 && y >= 0 && y <= 9) {
                label.setText("0" + String.valueOf((dT / 60000) % 1000) + ":" + String.valueOf((dT / 1000) % 60) + "," + String.valueOf((dT) % 1000));
            } else if (x >= 0 && x <= 9 && y > 9) {
                label.setText(String.valueOf((dT / 60000) % 1000) + ":0" + String.valueOf((dT / 1000) % 60) + "," + String.valueOf((dT) % 1000));
            } else if (x > 9 && y > 9) {
                label.setText(String.valueOf((dT / 60000) % 1000) + ":" + String.valueOf((dT / 1000) % 60) + "," + String.valueOf((dT) % 1000));
            }
        }

    }
}

See How to use Swing Timers for more details



来源:https://stackoverflow.com/questions/29596286/stopwatch-unpause-doesnt-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!