We want to add a menu to our 2D game.
So far, we had our game loop running on the main thread while the EventQueue from Swing was handling key inputs to our game. Now since we want to add the menu, we no longer can simply start the gameloop from our main method.
The problem: We don't know how to start the gameloop in such a way that the EventQueue keeps receiving the KeyEvents.
Here's what we have in our menu:
startGameButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
CardLayout cl = (CardLayout)Menu.this.getParent().getLayout();
cl.show(Menu.this.getParent(), "Game");
}
});
So clicking the startGameButton removes the menu from the JFrame and shows the game itself, but the game is frozen, because we don't start the gameloop from our main thread anymore. We think that it's here, that we need to start the gameloop.
So we created a new Thread that will run our gameloop to prevent freezing our UI:
startGameButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
CardLayout cl = (CardLayout)Menu.this.getParent().getLayout();
cl.show(Menu.this.getParent(), "Game");
Thread gameThread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
game.update();
game.repaint();
Thread.sleep(10);
}
}
});
gameThread.start();
}
});
This works perfectly, the game shows up, and it's running... BUT: Our KeyEvents are no longer firing. Somehow the EventQueue is not picking up keystrokes anymore and we don't know why.
Having the exact same code (creating the Thread and starting it) in the main method without using the CardLayout menu works perfectly fine, also getting KeyEvents. So we think the problem is somewhere in using the CardLayout but we don't know what it is exactly.
Thanks to D-Klotz for the comment about the input maps problem. Even though it was not the correct solution for my problem, it caused me to notice someting:
When switching from the JFrame to another window and then back, the KeyListener was working fine again. So the only line of code missing in the actionPerformed method was:
game.requestFocus();
Works like a charm. Thanks for your help again.
Related
NOTE: I work a lot of hours and research google and stackoverflow but I cannot find answer.
I use Thread.sleep() in a JDialog and it freezes all other JDialog, JFrame and threads.
My example code:
public Guitest()
{
setSize(300,300);
// create a JDialog that make guitest wait
MyDialog dlg = new MyDialog();
dlg.setSize(100,100);
dlg.setVisible(true);
while(dlg.isWait())
{
try
{
Thread.sleep(1000);
} catch (InterruptedException ex)
{
Logger.getLogger(Guitest.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("waiting mydialog");
}
}
class MyDialog extends JDialog
{
boolean wait = true;
JButton btn = new JButton("OK");
public MyDialog()
{
setSize(50,50);
btn.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
wait=false;
}
});
add(btn);
}
public boolean isWait()
{
return wait;
}
}
In this situation JDialog does not appear correctly:
inccorect appear jdialog
But it must be apper this:
true appear jdialog
How can I solve this problem. I want to make main thread wait another thread.And someone can correct my sample code or share a sample code with this situation.
IMHO, it appears like you have just one running thread. At first, we draw our JDialog, after that, you sleep your main thread because of the wait flag.
ie. you can't execute your button action listener, thus you can't awake your thread.
Hope it helps understanding.
Thread.Sleep() just sleeps the current thread (i.e. stops it from doing anything, such as redrawing, processing clicks etc), which in your case is the UI thread.
You need to use a worker thread. Any main work that needs to be done that may take a larger amount of time needs to be done in its own thread, and this is the thread that you will want to sleep. It is currently ran alongside the UI components and so this is why you're seeing them freezing.
A good reference is the documentation for concurrency for swing http://docs.oracle.com/javase/tutorial/uiswing/concurrency/
The following may be useful too:
http://java.sun.com/developer/technicalArticles/Threads/swing/
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
I have a Swing application where I wish to add some delay. I have a close button, which on clicking should display the JTextArea which displays "Closing database connections...." and then execute Database.databaseClose() method and System.exit(). I have tried using Thread.sleep() method as in the code below for the delay. When I execute the program, the screen freezes for 2 seconds and then closes without displaying the JTextArea. The close button and JTextArea is added to JFrame directly.
What I want is that on clicking the close button, the JTextArea should be displayed immediately and then the application should delay for 2 seconds before finally implementing the Database.databaseClose() method and exiting the program. The Database.databaseClose() method works just fine.
I am a beginner at Swings and would greatly appreciate it if anyone could modify the code to implement the requirement above. Thanks!
Here's the code snippet:
JButton btnClose = new JButton("Close");
btnClose.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JTextArea txtrClosingDatabaseConnections = new JTextArea();
txtrClosingDatabaseConnections.setText("\r\n\tClosing database connections....");
getContentPane().add(txtrClosingDatabaseConnections);
validate();
repaint();
/*
try
{
Thread.sleep(2000);
}
catch (InterruptedException e2)
{
e2.printStackTrace();
}
*/
try
{
Database.databaseClose();
}
catch (Exception e1)
{
e1.printStackTrace();
}
System.exit(0);
}
});
getContentPane().add(btnClose);
Hej, this is an example method that initializes an JMenuBar on a JFrame in Swing.
private JMenuBar initMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
exitApp = new JMenuItem("Exit App");
exitApp.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Timer t = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
JOptionPane.showMessageDialog(getParent(), "Closing App in 2 Seconds");
t.start();
}
});
fileMenu.add(exitApp);
menuBar.add(fileMenu);
return menuBar;
}
May it will help you. It creates an JOptionPane, which must be closed by clicking OK, then the JFrame will be closed after 2 seconds.
Your code is executing on the Event Dispatch Thread, so you can't use a Thread.sleep() since that will block the EDT and prevent it from repainting the GUI.
You need to use a separate Thread for you database processing. Read the section from the Swing tutorial on Concurrency for more information as well as a solution that uses a SwingWorker to manager this Thread for you.
The Timer is the solution. The Swing timer's task is performed in the event dispatch thread. This means that the task can safely manipulate components, but it also means that the task should execute quickly.
You can use Swing timers in two ways:
To perform a task once, after a delay.
For example, the tool tip manager uses Swing timers to determine when to show a tool tip and when to hide it.
To perform a task repeatedly.
For example, you might perform animation or update a component that displays progress toward a goal.
Please go through http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html for more details.
I have a game that runs off of JPanel which has on it many other things which have their own independent timers and such. It seems that when I try to remove the panel from my frame to replace it with another JPanel it refuses to actually end all of its own processes. So even if I am able to remove it from the screen of the panel by removing it and setting it null, its processes are still going off in the background, IE the music and the stuff flying around.
What i need to know is some solution as to how to completely kill this JPanel and terminate its life to its entirety.
Seems not many people have run into this problem.
I remember having that issue in my own game..
Simply create some custom method i.e destroy() which will stop all timers gameloops music etc.
i.e
MyPanel panel=new MyPanel();
...
panel.destory();//stop music, timers etc
frame.remove(panel);
//refresh frame to show changes
frame.revalidate();
frame.repaint();
where panel would be:
class MyPanel extends JPanel {
private Timer t1,t2...;
//this method will terminate the game i.e timers gameloop music etc
void destroy() {
t1.stop();
t2.stop();
}
}
Alternatively you could make your Swing Timers observers of sorts by making it check each time whether the panel is visible and if not it should stop executing. This would though now of course cause you to create a timer which will only start the others once the panel becomes visible:
class MyPanel extends JPanel {
private Timer t1,t2,startingTimer;
MyPanel() {
t1=new Timer(60,new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
if(!MyPanel.this.isVisible()) {//if the panel is not visible
((Timer)(ae.getSource())).stop();
}
}
});
startingTimer=new Timer(100,new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
if(MyPanel.this.isVisible()) {//if the panel is visible
t1.start();//start the timers
t2.start();
((Timer)(ae.getSource())).stop();//dont forget we must stop this timer now
}
}
});
startingTimer.start();//start the timer which will check when panel becomes visible and start the others as necessary
}
}
now all you would do is:
frame.remove(panel);//JPanel timers should also see panel is no more visible and timer will stop
//refresh frame to show changes
frame.revalidate();
frame.repaint();
Try this:
myFrame.getContentPane().remove(myPanel);
myFrame.validate();
Make sure your music and other components are within the panel so they are removed as well.
I am trying to move an label/icon & button from one place to another place using setLocation method but for some reason the objects are moving but not in motion. So if any one knows how to do this thing please tell me.
How to create a label or any component that moves from one place to another place in JFrame?
Below I have shown the code:
jl = JLabel
jf = JFrame
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==jbtn)
{
for(int i=0;i<=30;i++)
{
jl.setLocation(100,100+i);
jl.repaint();
jf.repaint();
try
{
Thread.sleep(50);
}
catch(Exception ae)
{
ae.printStackTrace();
}
}
}
}
Essentially you are blocking the thread (the Event Dispatching Thread) responsible for painting the updates
You might like to have a read of java timer change delay with button and Does displaying Java GUI requires some special treatment? which shows animation in swing and discusses the importance of the EDT.
I am trying to display a Loading Image in a new JFrame when the User clicks a particular button in my application.The JFrame is displayed,but it shows nothing!,also with a WHITE background,whereas all the JFrames have a grey default background.What is Wrong here?
stop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
analyzer.running=false;
JFrame Load1=new JFrame("Load1");
ImageIcon icon1=new ImageIcon("./ajax-loader.gif");
System.out.println(icon1.getIconHeight());
Load1.add(new JLabel("Retrieving...", icon1, JLabel.CENTER),BorderLayout.CENTER);
Load1.pack();
Load1.setSize(400,400);
Load1.setVisible(true);
System.out.println("Start Processing");
parser.parse(); // Time Consuming method
nw_Creator.create();
System.out.println("End Processing");
Load1.setVisible(false);
home.setVisible(false);
screen2.setVisible(true);
}
});
Do not put time consuming parts in an event handler or any method running in the event dispatch thread. You may want to use a swing worker instead.
What is happening is that you are never releasing the UI thread, so your JFrame is never painted. Since all graphics operations are done on the UI thread, you must release it, do your calculations, then close the frame if you want the jframe to display anything.