求助:Java GUI用户自定义倒计时计时器开发问题
Fixing Your Java GUI Countdown Timer Issues
Hey there! Let's work through the problems you're facing with your countdown timer. I've looked over your code and spotted a few key issues that are causing the bugs you mentioned. Let's break them down and fix them one by one.
Key Issues in Your Current Code
- Nested ActionListener on Start Button: You added an
ActionListenerinside anotherActionListenerfor the start button. That means the first click only registers the second listener, and the timer logic won't run until the second click—this is why some inputs seem unresponsive. - Broken Duration Calculation: Right now, you're overwriting the
durationvariable each time you check a time field, instead of adding up hours, minutes, and seconds together. For example, if you enter both hours and minutes, the minutes will overwrite the hours value entirely. Also, variables likecount_hour,count_min, andcount_secaren't declared anywhere, which would cause compilation errors. - Timer Initialization Timing: The
Timeris initialized inside the first button click listener, so when you first click "Start",timeris null and callingtimer.isRunning()would throw aNullPointerException.
Fixed Code with Combined Time Support
Here's the revised code that fixes all these issues and lets you combine hours, minutes, and seconds:
import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.time.Duration; import java.time.LocalDateTime; public class TestPane extends JFrame { private LocalDateTime startTime; private JLabel timerLabel; private Timer timer; private JButton startbtn; private JTextField timeSet; private JTextField minSet; private JTextField secSet; public TestPane() { setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(400, 400); getContentPane().setLayout(new CardLayout(0, 0)); JPanel panel = new JPanel(); getContentPane().add(panel, "name_87856346254020"); panel.setLayout(null); timerLabel = new JLabel("00h 00m 00s"); timerLabel.setBounds(59, 48, 194, 14); panel.add(timerLabel); startbtn = new JButton("Start"); startbtn.setBounds(124, 187, 89, 23); panel.add(startbtn); timeSet = new JTextField(); timeSet.setBounds(106, 85, 86, 20); panel.add(timeSet); timeSet.setColumns(10); JLabel lblHours = new JLabel("Hours"); lblHours.setBounds(29, 88, 46, 14); panel.add(lblHours); JLabel lblMinss = new JLabel("Mins"); lblMinss.setBounds(29, 114, 46, 14); panel.add(lblMinss); JLabel lblSecs = new JLabel("Secs"); lblSecs.setBounds(29, 139, 46, 14); panel.add(lblSecs); minSet = new JTextField(); minSet.setBounds(75, 116, 86, 20); panel.add(minSet); minSet.setColumns(10); secSet = new JTextField(); secSet.setBounds(75, 147, 86, 20); panel.add(secSet); secSet.setColumns(10); // Initialize timer here instead of inside the button listener timer = new Timer(1000, new ActionListener() { // Use 1000ms (1 second) for smoother updates @Override public void actionPerformed(ActionEvent e) { LocalDateTime now = LocalDateTime.now(); Duration runningTime = Duration.between(startTime, now); Duration totalDuration = getTotalDuration(); Duration timeLeft = totalDuration.minus(runningTime); if (timeLeft.isZero() || timeLeft.isNegative()) { timeLeft = Duration.ZERO; timer.stop(); startbtn.setText("Start"); JOptionPane.showMessageDialog(null, "Time's up!"); // Add end reminder } timerLabel.setText(format(timeLeft)); } }); // Single ActionListener for the start button startbtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (timer.isRunning()) { timer.stop(); startbtn.setText("Start"); } else { startTime = LocalDateTime.now(); timer.start(); startbtn.setText("Stop"); } } }); setVisible(true); // Move this to the end so all components are initialized first } // Calculate total duration by adding hours, minutes, seconds together private Duration getTotalDuration() { long hours = parseInput(timeSet.getText(), 0); long mins = parseInput(minSet.getText(), 0); long secs = parseInput(secSet.getText(), 0); return Duration.ofHours(hours) .plusMinutes(mins) .plusSeconds(secs); } // Helper method to safely parse text inputs to numbers private long parseInput(String input, long defaultValue) { try { return Long.parseLong(input.trim()); } catch (NumberFormatException ex) { return defaultValue; // Return 0 if input is empty or invalid } } protected String format(Duration duration) { long hours = duration.toHours(); long mins = duration.minusHours(hours).toMinutes(); long seconds = duration.minusHours(hours).minusMinutes(mins).getSeconds(); return String.format("%02dh %02dm %02ds", hours, mins, seconds); } public static void main(String args[]) { SwingUtilities.invokeLater(() -> new TestPane()); // Use SwingUtilities for thread safety } }
What Changed & Why
- Removed Nested ActionListener: Now there's only one listener on the start button, so clicking it immediately toggles the timer state.
- Combined Duration Calculation: The new
getTotalDuration()method adds up hours, minutes, and seconds from all three fields instead of overwriting the duration. Empty or invalid inputs default to 0. - Early Timer Initialization: The
Timeris created when the frame initializes, so no more null pointer errors when checking if it's running. - Added Time's Up Reminder: A
JOptionPanepops up when the countdown finishes, as per your requirement. - Thread-Safe Initialization: Used
SwingUtilities.invokeLater()inmainto ensure Swing components are created on the Event Dispatch Thread (best practice for Swing apps). - Smoother Updates: Changed the timer interval to 1000ms (1 second) so the display updates once per second, which is more intuitive for a countdown.
How to Use It
- Enter any combination of hours, minutes, and seconds (leave fields empty if you don't need that unit).
- Click "Start" to begin the countdown; click "Stop" to pause it.
- When the timer reaches 0, a popup will notify you that time's up.
内容的提问来源于stack exchange,提问作者Syed Sadman
相关产品推荐
相关产品推荐

