3

我正在尝试制作一个简单的 java 应用程序来计算时间,并能够停止和启动计时器。但是,标签不会更新,当我按开始时,它会冻结。

你能帮我找出问题所在吗?

package random;

import javax.swing.JFrame;

public class Timer {
boolean shouldCount=false;
int int_sec=0;
int int_min=0;
int int_mil=0;
public static void main(String[] args) {
    TimeFrame t = new TimeFrame();
    JFrame f = new JFrame("Timer");
    f.setSize(300,200);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setLocationRelativeTo(null);
    f.getContentPane().add(t);
    f.setVisible(true);
}

public void count(){
    TimeFrame t = new TimeFrame();
    if(shouldCount){
        long now = System.currentTimeMillis();
        while(true){
            if(System.currentTimeMillis()-now>=100){
                now=System.currentTimeMillis();
                String sec = Integer.toString(int_sec);
                String min = Integer.toString(int_min);
                String mil = Integer.toString(int_mil);
                t.update(sec,int_sec,min,mil,int_mil);
                int_mil++;
                if(int_mil>9){
                    int_mil=0;
                    int_sec++;
                    if(int_sec>=60){
                        int_sec=1;
                        int_min++;
                    }
                }
            }
        }
    }
}
}

这是 TimeFrame.java

    package random;

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JLabel;
    import javax.swing.JPanel;

    public class TimeFrame extends JPanel{
    JLabel time = new JLabel("Time goes here", JLabel.CENTER);
    Timer t = new Timer();
    JButton pause = new JButton ("Pause");
     JButton start = new JButton ("Start");
    public TimeFrame(){

         start.addActionListener(new starts());
         pause.addActionListener(new starts());
         add(time);
         add(start);
         add(pause);
    }
    public void update(String sec,int s, String min,String mil,int m){
        if (s<=10){
            sec="0"+sec;
        }
        System.out.println(min+":"+sec+","+mil);
        time.setText(min+":"+sec+","+mil);

    }
    public class starts implements ActionListener{
        public void actionPerformed(ActionEvent event){
            if(event.getSource() == start){
                t.shouldCount=true;
            }else{
                t.shouldCount=false;
            }
            t.count();
        }
    }
}
4

2 回答 2

3

问题是您的应用程序中只有一个线程。您应该至少有两个:一个用于更新文本的 UI,另一个用于计算时间。

如果您只有一个线程,它会挂在 while(true) 循环中,并且 Swing 永远不会更新视图。

我使用两个线程重构了您的代码:

  1. 一次计数直到时间结束并更新字段以将时间保留在内存中

  2. 另一个使用 java.util.Timer#scheduleAtFixedRate() 方法的方法每 100 毫秒调用一次以更新视图。

Timer.java(避免像 Java API 中的那样命名类)

    public class Timer {

        boolean shouldCount=false;
        int int_sec=0;
        int int_min=0;
        int int_mil=0;

        public Timer() {
        }

        public static void main(String[] args) {
            TimeFrame t = new TimeFrame();
            JFrame f = new JFrame("Timer");
            f.setSize(300,200);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setLocationRelativeTo(null);
            f.getContentPane().add(new TimeFrame());
            f.setVisible(true);
        }

        public void count(){
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        long now = System.currentTimeMillis();
                        while(true){
                        if(shouldCount){
                            if(System.currentTimeMillis()-now>=100){
                                now=System.currentTimeMillis();
                                int_mil++;
                                if(int_mil>9){
                                     int_mil=0;
                                     int_sec++;
                                     if(int_sec>=60){
                                          int_sec=1;
                                          int_min++;
                                     }
                                }
                            }       
                        }
                    }               
                }
            });
            thread.start();
        }
    }
}

TimeFrame(我宁愿叫它,TimePanel因为它扩展了JPanel

public class TimeFrame extends JPanel{
    JLabel time;
    Timer t ;
    JButton pause ;
    JButton start ;
    public TimeFrame(){

        t= new Timer(this);

        time = new JLabel("Time goes here", JLabel.CENTER);
        pause = new JButton ("Pause");
        start = new JButton ("Start");

        start.addActionListener(new starts());
        pause.addActionListener(new starts());
        add(time);
        add(start);
        add(pause);

        java.util.Timer updateTimer= new java.util.Timer();
        updateTimer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            t.update(int_sec,int_min,int_mil);
        }
        }, 0, 100);
    }    

    public void update(int s, int minute,int m){
        String sec = Integer.toString(s);
        String min = Integer.toString(minute);
        String mil = Integer.toString(m);
    if (s<=10){
            sec="0"+sec;
        }

        System.out.println(min+":"+sec+","+mil);
        time.setText(min+":"+sec+","+mil);
    }


   public class starts implements ActionListener{
    boolean firstTime=true;
    public void actionPerformed(ActionEvent event){
        if (firstTime){
            t.count();
            firstTime = false;
        }
        if(event.getSource() == start){
            t.shouldCount=true;
        }else{
            t.shouldCount=false;
        }
    }
}

}
于 2013-09-20T22:39:37.810 回答
1

问题是您的 while 循环与您的接口线程相关联。当您单击开始按钮时,它会调用 Timer.count(),然后进入无限循环,导致界面卡住并且永远不会更新。

使用 java.util.Timer 类更好的假设可能高估了针对这个特定问题的类功能。它不包含暂停方法,当您想要暂停它时,您必须重新创建计时器,这可能会给加起来带来一些困难的挑战。

我要做的是让您的计时器实现可运行接口并使用线程来密切关注您当前的时间。这是我要做的改变

请注意,我将您的字段设为私有。将您的字段设为私有(如果它们应该是私有的)并使用 getter 和 setter 授予对它们的访问权限是正确的做法。例如:getCurrentTime()

定时器.java:

package random;

import javax.swing.JFrame;

public class Timer implements Runnable {

    private Thread runThread;
    private boolean running = false;
    private boolean paused = false;
    private TimeFrame timeFrame;
    private long summedTime = 0;

    public Timer(TimeFrame timeFrame) {
        this.timeFrame = timeFrame;
    }

    public static void main(String[] args) {
        TimeFrame t = new TimeFrame();
        JFrame f = new JFrame("Timer");
        f.setSize(300,200);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLocationRelativeTo(null);
        f.getContentPane().add(t);
        f.setVisible(true);
    }

    public void startTimer() {
        running = true;
        paused = false;
        // start the thread up
        runThread = new Thread(this);
        runThread.start();
    }

    public void pauseTimer() {
        // just pause it
        paused = true;
    }

    public void stopTimer() {
        // completely stop the timer
        running = false;
        paused = false;
    }

    @Override
    public void run() {
        long startTime = System.currentTimeMillis();
        // keep showing the difference in time until we are either paused or not running anymore
        while(running && !paused) {
            timeFrame.update(summedTime + (System.currentTimeMillis() - startTime));
        }
        // if we just want to pause the timer dont throw away the change in time, instead store it
        if(paused)
            summedTime += System.currentTimeMillis() - startTime;
        else 
            summedTime = 0;
    }
}

TimeFrame.java:

package random;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class TimeFrame extends JPanel{
    private JLabel time = new JLabel("Time goes here", JLabel.CENTER);
    private Timer timer;
    private JButton pause = new JButton ("Pause");
    private JButton start = new JButton ("Start");

    public TimeFrame(){
        timer = new Timer(this);
         start.addActionListener(new starts());
         pause.addActionListener(new starts());
         add(time);
         add(start);
         add(pause);
    }
    public void update(long dT){
        // convert milliseconds into other forms
        time.setText(String.valueOf((dT/6000)%1000)+":"+String.valueOf((dT/1000)%1000)+","+String.valueOf((dT)%1000));
    }
    public class starts implements ActionListener{
        public void actionPerformed(ActionEvent event){
            if(event.getSource() == start){
                timer.startTimer();
            }else{
                timer.pauseTimer();
            }
        }
    }
}
于 2013-09-20T23:47:44.297 回答