Java 计时器/秒表 GUI
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18926839/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Timer/Stopwatch GUI
提问by Quar
I am trying to make a simple java application that counts time, with the ability to stop and start the timer. However, the label won't update, and when I press start, it freezes.
我正在尝试制作一个简单的 java 应用程序来计算时间,并能够停止和启动计时器。但是,标签不会更新,当我按开始时,它会冻结。
Could you help me figure out what the problem is?
你能帮我弄清楚是什么问题吗?
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++;
}
}
}
}
}
}
}
And here is TimeFrame.java
这是 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();
}
}
}
采纳答案by Marc
The problem is that you have only one thread in your application. You should have at least two : one for the UI that will update the text and one for the computation of the time.
问题是您的应用程序中只有一个线程。您应该至少有两个:一个用于更新文本的 UI,另一个用于计算时间。
If you have only one thread it hangs in the while(true) loop and Swing never get to update the view.
如果您只有一个线程,它会挂在 while(true) 循环中,并且 Swing 永远不会更新视图。
I refactored your code using two threads :
我使用两个线程重构了您的代码:
One counting until the end of time and updating the fields to keep time in memory
Another one that uses a java.util.Timer#scheduleAtFixedRate() method wich is invoqued every 100 millisec to update the view.
一个计数直到时间结束并更新字段以在内存中保留时间
另一种使用 java.util.Timer#scheduleAtFixedRate() 方法,每 100 毫秒调用一次以更新视图。
Timer.java
(avoid naming classes like ones in the Java API)
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
(I would rather call it TimePanel
since it extends JPanel
)
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;
}
}
}
}
回答by ug_
The problem is your while loop is tied to your interface thread. When you click the start button it calls Timer.count() and then goes into an infinite loop causing the interface to be stuck and never update.
问题是您的 while 循环与您的接口线程相关联。当您单击开始按钮时,它会调用 Timer.count(),然后进入无限循环,导致界面卡住并且永远不会更新。
The assumptions that using the java.util.Timer class is better may be over estimating that classes functionality for this specific problem. It does not contain a pause method and you have to recreate the timer when you want to pause it, causing some possible difficult challenges for adding up time.
使用 java.util.Timer 类更好的假设可能高估了该特定问题的类功能。它不包含暂停方法,当你想暂停它时,你必须重新创建计时器,这可能会给增加时间带来一些困难的挑战。
What I would do is make your timer implement the runnable interface and use a thread to keep tabs on your current time. Heres the changes I would make
我会做的是让你的计时器实现可运行的接口并使用一个线程来保持你当前时间的标签。这是我要做的改变
Notice I made your fields private. It is proper practice to make your fields private (if they should be) and use getters and setters to grant access to them. eg: getCurrentTime()
请注意,我将您的字段设为私有。将您的字段设为私有(如果它们应该是)并使用 getter 和 setter 授予对它们的访问权限是正确的做法。例如:getCurrentTime()
Timer.java:
定时器.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:
时间框架.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();
}
}
}
}