Java – 更新现有的秒表代码以包括倒计时

@HovercraftFullofEels非常友好地通过为我提供以下代码的基础来帮助我,我对其进行了一些修改(标记为“行完全添加”注释和最后的巨大注释,其中包含要放置在某处的代码文件)。

原始文件是一个简单的秒表,我的修改包括3个JTextFields.that接收分钟,秒和CENTI秒(1厘秒= 1/100秒)。 我想要包含一个“提交”按钮,它允许程序读取这3个文本字段的输入。 我编写了单击“提交”时要调用的方法的代码(包含在最后的巨型注释中)。 单击它后,我希望程序立即从秒表开始时的那些值开始倒计时,而不是从单击按钮开始倒计时。 例如,如果秒表在用户点击“提交”并且输入时间为25分钟时已经运行了20分钟,则将开始5分钟的倒计时。

如果这仍然令人困惑,那么你真正需要知道的是我的方法以一条线结束,该线提供了我希望倒计时开始的毫秒表示,此时我想要倒计时来替换秒表。 我还想删除“暂停”和“停止”按钮,但不是“开始”按钮(你会认为它们很容易删除,但我删除了我认为合适并在编译时收到错误)并替换他们使用单个“提交”按钮。

import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.Font; //line completely added import javax.swing.*; @SuppressWarnings("serial") public class MyTimer2 extends JPanel implements GuiTimer { private static final String TIME_FORMAT = "%02d:%02d:%02d"; //changed from "%03d:%03d" private static final int EXTRA_WIDTH = 50; private JLabel timerLabel = new JLabel(); private TimerControl timerControl = new TimerControl(this); JTextField minsField, secsField, centisField; //line completely added - should this be private? JLabel colon, period; //line completely added - should this be private? public MyTimer2() { JPanel topPanel = new JPanel(); topPanel.add(timerLabel); timerLabel.setFont(new Font("Arial", Font.BOLD, 64)); //line completely added minsField = new JTextField("", 2); secsField = new JTextField("", 2); centisField = new JTextField("", 2); colon = new JLabel(":"); period = new JLabel("."); JPanel centerPanel = new JPanel(); centerPanel.add(minsField); //line completely added centerPanel.add(colon); //line completely added centerPanel.add(secsField); //line completely added centerPanel.add(period); //line completely added centerPanel.add(centisField); //line completely added JPanel bottomPanel = new JPanel(); //line completely added bottomPanel.add(new JButton(timerControl.getStartAction())); //changed from centerPanel bottomPanel.add(new JButton(timerControl.getStopAction())); //changed from centerPanel setLayout(new BorderLayout()); add(topPanel, BorderLayout.PAGE_START); add(centerPanel, BorderLayout.CENTER); add(bottomPanel, BorderLayout.PAGE_END); //line completely added setDeltaTime(0); } @Override public void setDeltaTime(int delta) { int mins = (int) delta / 60000; // line completely added int secs = ((int) delta % 60000) / 1000; // %60000 added int centis = ((int) delta % 1000) / 10; // / 10 added timerLabel.setText(String.format(TIME_FORMAT, mins, secs, centis)); // mins added; mSecs changed to centis } @Override public Dimension getPreferredSize() { Dimension superSz = super.getPreferredSize(); if (isPreferredSizeSet()) { return superSz; } int prefW = superSz.width + EXTRA_WIDTH; int prefH = superSz.height; return new Dimension(prefW, prefH); } private static void createAndShowGui() { MyTimer2 mainPanel = new MyTimer2(); JFrame frame = new JFrame("MyTimer2"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //changed from DISPOSE_ON_CLOSE frame.getContentPane().add(mainPanel); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGui(); } }); } } interface GuiTimer { public abstract void setDeltaTime(int delta); } @SuppressWarnings("serial") class TimerControl { private static final int TIMER_DELAY = 10; private long startTime = 0; private long pauseTime = 0; private Timer timer; private GuiTimer gui; private StartAction startAction = new StartAction(); private StopAction stopAction = new StopAction(); public TimerControl(GuiTimer gui) { this.gui = gui; } public Action getStopAction() { return stopAction; } public Action getStartAction() { return startAction; } enum State { START("Start", KeyEvent.VK_S), PAUSE("Pause", KeyEvent.VK_P); private String text; private int mnemonic; private State(String text, int mnemonic) { this.text = text; this.mnemonic = mnemonic; } public String getText() { return text; } public int getMnemonic() { return mnemonic; } }; private class StartAction extends AbstractAction { private State state; public StartAction() { setState(State.START); } public final void setState(State state) { this.state = state; putValue(NAME, state.getText()); putValue(MNEMONIC_KEY, state.getMnemonic()); } @Override public void actionPerformed(ActionEvent e) { if (state == State.START) { if (timer != null && timer.isRunning()) { return; // the timer's already running } setState(State.PAUSE); if (startTime <= 0) { startTime = System.currentTimeMillis(); timer = new Timer(TIMER_DELAY, new TimerListener()); } else { startTime += System.currentTimeMillis() - pauseTime; } timer.start(); } else if (state == State.PAUSE) { setState(State.START); pauseTime = System.currentTimeMillis(); timer.stop(); } } } private class StopAction extends AbstractAction { public StopAction() { super("Stop"); int mnemonic = KeyEvent.VK_T; putValue(MNEMONIC_KEY, mnemonic); } @Override public void actionPerformed(ActionEvent e) { if (timer == null) { return; } timer.stop(); startAction.setState(State.START); startTime = 0; } } private class TimerListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { long time = System.currentTimeMillis(); long delta = time - startTime; gui.setDeltaTime((int) delta); } } } /*not sure where this will go, but this is the code for clicking "Submit" //upon clicking "Submit"... public void actionPerformed(ActionEvent e) { String minsStr = minsField.getText(); String secsStr = secsField.getText(); String centisStr = centisField.getText(); int minsInput = Integer.parseInt(minsStr); int secsInput = Integer.parseInt(secsStr); int centisInput = Integer.parseInt(centisStr); long millis = minsInput * 60000 + secsInput * 1000 + centisInput * 10; long millisCountdown = millis - delta; //where "delta" is elapsed milliseconds if(millisCountdown < 0) JOptionPane.showMessageDialog("Invalid time entered."); else //then immediately change from stopwatch to countdown beginning from millisCountdown and ending at 00:00:00 minsField.setText(""); //clear minsField secsField.setText(""); //clear secsField centisField.setText(""); //clear centisField } */ 

如果有人能帮助我,我会非常感激。 不幸的是,我不了解Hovercraft的大部分代码,所以我不知道在我已经完成的工作之后去哪里。

谢谢!

编辑:这是@ MadProgrammer代码的更新版本:

 import java.awt.EventQueue; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.Font; import java.time.Duration; import java.time.LocalTime; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.Timer; import javax.swing.JOptionPane; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class StopWatch { public static void main(String[] args) { new StopWatch(); } public StopWatch() { 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 static class TestPane extends JPanel { protected static final String TIME_FORMAT = "%02d:%02d.%02d"; private LocalTime startTime; private LocalTime targetTime; private JLabel label; private JTextField minsField, secsField, centisField; private JButton start, submit; private Timer timer; public TestPane() { JPanel topRow = new JPanel(); JPanel centerRow = new JPanel(); JPanel bottomRow = new JPanel(); label = new JLabel(formatDuration(Duration.ofMillis(0))); topRow.add(label); minsField = new JTextField("", 2); secsField = new JTextField("", 2); centisField = new JTextField("", 2); centerRow.add(minsField); centerRow.add(secsField); centerRow.add(centisField); start = new JButton("Start"); start.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (!timer.isRunning()) { startTime = LocalTime.now(); timer.start(); } } }); bottomRow.add(start); submit = new JButton("Submit"); submit.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (timer.isRunning()) { timer.stop(); Duration runningTime = Duration.between(startTime, LocalTime.now()); // Subtract the required amount of time from the duration String minsStr = minsField.getText(); String secsStr = secsField.getText(); String centisStr = centisField.getText(); if(minsStr.matches("\\d+$") && secsStr.matches("\\d+$") && centisStr.matches("\\d+$")) { int minsInput = Integer.parseInt(minsStr); int secsInput = Integer.parseInt(secsStr); int centisInput = Integer.parseInt(centisStr); if(minsInput >= 0 && secsInput >= 0 && secsInput = 0 && centisInput  0) { // When the timer is to end... targetTime = LocalTime.now().plus(runningTime); } else { JOptionPane.showMessageDialog(null, "Invalid time entered."); } } else { timer.start(); JOptionPane.showMessageDialog(null, "Invalid time entered."); } } else { timer.start(); JOptionPane.showMessageDialog(null, "Invalid time entered."); } minsField.setText(""); secsField.setText(""); centisField.setText(""); } } }); bottomRow.add(submit); timer = new Timer(10, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (targetTime != null) { Duration duration = Duration.between(LocalTime.now(), targetTime); if (duration.toMillis() <= 0) { duration = Duration.ofMillis(0); timer.stop(); targetTime = null; } label.setText(formatDuration(duration)); } else { // Count up... Duration duration = Duration.between(startTime, LocalTime.now()); label.setText(formatDuration(duration)); } } }); setLayout(new BorderLayout()); label.setFont(new Font("Arial", Font.BOLD, 64)); add(topRow, BorderLayout.PAGE_START); add(centerRow, BorderLayout.CENTER); add(bottomRow, BorderLayout.PAGE_END); } protected String formatDuration(Duration duration) { long mins = duration.toMinutes(); duration = duration.minusMinutes(mins); long seconds = duration.toMillis() / 1000; duration = duration.minusSeconds(seconds); long centis = duration.toMillis() / 10; return String.format(TIME_FORMAT, mins, seconds, centis); } } } 

这使用新的Java 8 Time API来简化过程,允许您计算两个时间点之间的持续时间以及算术

请参阅日期和时间类

在此处输入图像描述

 import java.awt.EventQueue; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.time.Duration; import java.time.LocalTime; 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 StopWatch { public static void main(String[] args) { new StopWatch(); } public StopWatch() { 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 static class TestPane extends JPanel { protected static final String TIME_FORMAT = "%02dh %02dm %02ds"; private LocalTime startTime; private LocalTime targetTime; private JLabel label; private JButton start; private Timer timer; public TestPane() { label = new JLabel(formatDuration(Duration.ofMillis(0))); start = new JButton("Start"); start.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (timer.isRunning()) { timer.stop(); Duration runningTime = Duration.between(startTime, LocalTime.now()); // Subtract the required amount of time from the duration runningTime = runningTime.minusSeconds(5); // No negative times if (runningTime.toMillis() > 0) { // When the timer is to end... targetTime = LocalTime.now().plus(runningTime); timer.start(); } } else { startTime = LocalTime.now(); timer.start(); } } }); timer = new Timer(500, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (targetTime != null) { Duration duration = Duration.between(LocalTime.now(), targetTime); if (duration.toMillis() <= 0) { duration = Duration.ofMillis(0); timer.stop(); targetTime = null; } label.setText(formatDuration(duration)); } else { // Count up... Duration duration = Duration.between(startTime, LocalTime.now()); label.setText(formatDuration(duration)); } } }); setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridwidth = GridBagConstraints.REMAINDER; add(label, gbc); add(start, gbc); } protected String formatDuration(Duration duration) { long hours = duration.toHours(); duration = duration.minusHours(hours); long mins = duration.toMinutes(); duration = duration.minusMinutes(mins); long seconds = duration.toMillis() / 1000; return String.format(TIME_FORMAT, hours, mins, seconds); } } } 

我还想删除“暂停”和“停止”按钮(你会认为它们很容易删除,但我删除了我认为合适并在编译时收到错误)并用单个“提交”按钮替换它们。

有关更多详细信息,请参阅使用JFC / Swing创建GUI

不幸的是,我不了解Hovercraft的大部分代码

我们为您提供的任何其他解决方案都将获得相同的结果。 您需要将需求分解为可管理的块,计算定时器如何向前移动,然后确定如何使其向后移动,然后找出如何组合这两个概念,以便从运行中减去目标值时间并向后移动。