问题
I am creating a Java Swing game and before every new game I would like to have a frame show up and countdown from 5 to 0 seconds.
While this is happening I would like the game in the background to wait until the countdown is complete. What would be the best way to make the game in the background wait?
I have tried Thread.sleep but this causes the The Event Dispatch Thread to sleep and the GUI not to update. However, it works the first time i run it but not the second.
Thankful for your help.
public class CountdownPresenterPanel {
JFrame mainFrame;
int currentNumber = 5;
JLabel textLabel;
CountdownPresenterPanel() {
mainFrame = new JFrame();
textLabel = new JLabel(String.valueOf(currentNumber), SwingConstants.CENTER);
textLabel.setFont(new Font("Arial", Font.BOLD, 55));
textLabel.setVerticalAlignment(SwingConstants.CENTER);
mainFrame.add(textLabel);
mainFrame.setUndecorated(true); // Remove bar including close, minimize
mainFrame.setSize(600,300);
mainFrame.setLocationRelativeTo(null);
mainFrame.setVisible(true);
Timer timer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(currentNumber > 0) {
currentNumber--;
textLabel.setText(String.valueOf(currentNumber));
} else {
mainFrame.setVisible(false);
mainFrame.dispose();
}
}
});
timer.setRepeats(true);
timer.start();
}
}
Solution
import java.awt.*;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
public class CountdownPresenterPanel {
private int currentNumber = 5;
private JLabel textLabel;
private final JDialog dialog;
CountdownPresenterPanel() {
Timer timer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(currentNumber > 0) {
currentNumber--;
textLabel.setText(String.valueOf(currentNumber));
} else {
dialog.dispose();
}
}
});
Frame window = new Frame();
dialog = new JDialog(window, "Alert", true);
textLabel = new JLabel(String.valueOf(currentNumber), SwingConstants.CENTER);
textLabel.setFont(new Font("Arial", Font.BOLD, 55)); // Increase the font-size
dialog.add(textLabel);
dialog.setSize(400, 200);
dialog.setLocationRelativeTo(null);
timer.setRepeats(true);
timer.start();
dialog.setUndecorated(true);
dialog.setVisible(true);
}
}
回答1:
Don't use a JFrame
, use some kind of modal dialog
Take a look at How to Make Dialogs for more details
回答2:
Seems that you want something like a SplashScreen. Have you looked to the SplasScreen Tutorial from Oracle? There Thread.sleep()
is used safely and it updates the graphics correctly. I think it to be your unique solution. The only way to let the undergoing application to wait is to use the same thread. The reason your Timer didn't work is because it creates another thread to run itself.
回答3:
Another option is to use a SwingWorker
:
import java.awt.*;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.beans.*;
import javax.swing.*;
public class CountdownPresenterPanel2 {
public static void main(String[] args) {
final JFrame frame = new JFrame();
final JDialog splashScreen = new JDialog(null, Dialog.ModalityType.DOCUMENT_MODAL);
final JLabel textLabel = new JLabel(String.valueOf(5), SwingConstants.CENTER);
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
textLabel.setFont(new Font("Arial", Font.BOLD, 55));
textLabel.setVerticalAlignment(SwingConstants.CENTER);
splashScreen.setUndecorated(true);
splashScreen.getContentPane().add(textLabel);
splashScreen.setSize(600, 300);
splashScreen.setLocationRelativeTo(null);
splashScreen.setVisible(true);
}
});
(new GamePanelInitTask() {
@Override protected void process(java.util.List<Integer> chunks) {
for (Integer value : chunks) {
textLabel.setText(value.toString());
}
}
@Override public void done() {
splashScreen.dispose();
try {
MainGamePanel p = get();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(p);
frame.setSize(320, 240);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
}
}).execute();
}
}
class GamePanelInitTask extends SwingWorker<MainGamePanel, Integer> {
@Override public MainGamePanel doInBackground() {
int currentNumber = 5;
MainGamePanel game = new MainGamePanel();
while (currentNumber > 0 && !isCancelled()) {
currentNumber--;
publish(currentNumber);
game.add(new JLabel(String.format("Label: %d", currentNumber)));
try {
Thread.sleep(1000); //dummy task
} catch (InterruptedException ie) {
ie.printStackTrace();
return null;
}
}
return game;
}
}
class MainGamePanel extends JPanel {
public MainGamePanel() {
super();
try {
Thread.sleep(1000); //dummy task
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
来源:https://stackoverflow.com/questions/24200634/swing-countdown-from-5-to-0