问题
@HovercraftFullofEels was kind enough to help me by providing me the basis for the following code, which I made some modifications to (marked by "line completely added" comments and the giant comment at the end, which contains code to be placed somewhere in the file).
The original file was a simple stopwatch, and my modifications are to include 3 JTextFields.that take in mins, secs, and CENTIseconds (1 centisecond = 1/100 of a second). I want to include a "Submit" button too, which allows the program to read the input of these 3 text fields. I wrote the code for the method to be invoked when "Submit" is clicked (included in the giant comment at the end). Upon clicking it, I want the program to immediately begin a countdown from those values starting from the time the stopwatch started rather than from the time of clicking the button. For example, if the stopwatch has been running for 20 minutes upon the user clicking "Submit" with an inputted time of 25 minutes, a 5-minute countdown would begin.
If this is still confusing, then all you really need to know is that my method ends with a line that provides a millisecond representation of where I want the countdown to begin, at which point I want the countdown to REPLACE the stopwatch. I also want to remove the "Pause" and "Stop" buttons, but not the "Start" button (you would think they would be easy to remove, but I removed what I thought was appropriate and received an error when compiling) and replace them with the single "Submit" button.
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
}
*/
If anyone could help me with this, I would greatly appreciate it. Unfortunately I don't understand the majority of Hovercraft's code, so I have no clue where to go after what I've already done.
Thank you!
EDIT: Here is the updated version of @MadProgrammer's code:
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 < 60 && centisInput >= 0 && centisInput < 100)
{
long millis = minsInput * 60000 + secsInput * 1000 + centisInput * 10;
runningTime = runningTime.minusMillis(millis);
timer.start();
// No negative times
if (runningTime.toMillis() > 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);
}
}
}
回答1:
This makes use of the new Java 8 Time API to simplify the process, allowing your to calculate durations between two points in time as well as arithmetic
See Date and Time Classes
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);
}
}
}
I also want to remove the "Pause" and "Stop" buttons (you would think they would be easy to remove, but I removed what I thought was appropriate and received an error when compiling) and replace them with the single "Submit" button.
Take a look at Creating a GUI With JFC/Swing for more details
Unfortunately I don't understand the majority of Hovercraft's code
And any other solution we provide you will have the same result. You need to break down you requirements into manageable chunks, work out how the timer moves forward, then work out how you can make it move backwards, then work out how you can combine the two concepts so you can subtract a target value from the running time and move it backwards.
来源:https://stackoverflow.com/questions/33512527/java-updating-existing-stopwatch-code-to-include-countdown