我正在尝试创建一个简单的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();
}
}
}
答案 0 :(得分:3)
问题是您的应用程序中只有一个线程。您应该至少有两个:一个用于更新文本的UI,另一个用于计算时间。
如果你只有一个线程挂在while(true)循环中,Swing永远不会更新视图。
我使用两个线程重构了你的代码:
一直计算到时间结束并更新字段以留出时间
另一个使用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;
}
}
}
}
答案 1 :(得分:1)
问题是你的while循环与你的界面线程有关。单击开始按钮时,它调用Timer.count(),然后进入无限循环,导致接口卡住并且永不更新。
使用java.util.Timer类更好的假设可能是过度估计此特定问题的类功能。它不包含暂停方法,您必须在想要暂停时重新创建计时器,从而导致一些可能的困难挑战,以增加时间。
我要做的是让你的计时器实现runnable接口,并使用一个线程来监视你当前的时间。继续我将做出的改变
注意我将您的字段设为私有。正确的做法是将您的字段设为私有字段(如果它们应该是),并使用getter和setter来授予对它们的访问权限。例如:getCurrentTime()
Timer.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();
}
}
}
}