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.
Related
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.
Currently I have a client that contains two panels... one is the main game and the other is a side panel containing tools. The side panel can be shown/hid (thus making the frame only show the game).
activateSidePanel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (sp) {
frame.remove(enhancedPanel);
frame.repaint();
frame.pack();
sp = false;
} else if (!sp) {
frame.add(enhancedPanel);
frame.repaint();
frame.pack();
sp = true;
}
}
});
That is my action listener for the button. The button hides correctly, however it doesn't show. When I click the button again it just makes the frame smaller and does not bring back the side panel. Confused on this one.
} else if (!sp) {
Why do test for !sp? A boolean can only have two values, so all you need is an if/else statement (without the test on the else.
Instead of removing/adding the panel I would try invoking the setVisible(false/true) method first.
If that doesn't work then then general code for removing/adding components is:
panel.add(...)
panel.revalidate();
panel.repaint();
You should not need to invoke pack() because you don't want the frame to keep resizing, you only want the main panel to become bigger/smaller.
The code snippet below sets text in a JLabel, which is added to a JPanel, which is attached to a JFrame. No matter what I do though (such as repaint(), revalidate(), etc) I cannot get the UI to update the text until the Action Listener is done.
I have never had this problem before, possible because I have never had to have several things happen in a single firing of Action Listener. What am I missing?
TL;DR Why does the following not update the text on the screen until it has finished firing the Action Listener, even if I put in repaint() after each listPanel.add()?
final JFrame guiFrame = new JFrame();
final JPanel listPanel = new JPanel();
listPanel.setVisible(true);
final JLabel listLbl = new JLabel("Welcome");
listPanel.add(listLbl);
startStopButton.addActionListener(new ActionListener(){#Override public void actionPerformed(ActionEvent event){
if(startStopButton.getText()=="Start"){
startStopButton.setVisible(false);
listPanel.remove(0);
JLabel listLbl2 = new JLabel("Could not contact”);
listPanel.add(listLbl2);
JLabel listLbl2 = new JLabel("Success”);
listPanel.add(listLbl2);
}
}
guiFrame.setResizable(false);
guiFrame.add(listPanel, BorderLayout.LINE_START);
guiFrame.add(startStopButton, BorderLayout.PAGE_END);
//make sure the JFrame is visible
guiFrame.setVisible(true);
EDIT:
I attempted to implement SwingWorker, but still the interface is not updating until the action interface finishes firing. Here is my SwingWorker code:
#Override
protected Integer doInBackground() throws Exception{
//Downloads and unzips the first video.
if(cameraBoolean==true)
panel.add(this.downloadRecording(camera, recording));
else
panel.add(new JLabel("Could not contact camera "+camera.getName()));
panel.repaint();
jframe.repaint();
return 1;
}
private JLabel downloadRecording(Camera camera, Recording recording){
//does a bunch of calculations and returns a jLabel, and works correctly
}
protected void done(){
try{
Date currentTime = new Timestamp(Calendar.getInstance().getTime().getTime());
JOptionPane.showMessageDialog(jframe, "Camera "+camera.getName()+" finished downloading at "+currentTime.getTime());
}catch (Exception e){
e.printStackTrace();
}
}
Basically, SwingWorker (as I implemented it) is not properly updating the JPanel and JFrame. If I try to do the repaint in the "done()", they are not updated either. What am I missing?
Additionally, as soon as the JOptionPane displays itself, no more panels can be added to my jframe. I am unsure what is causing that either.
The action listener is being executed on the Event Dispatch Thread. For tasks like that, consider using a SwingWorker.
This would allow you to process your logic without blocking the updates (and thus the repaints) of the JFrame.
At a high level, this is what I mean:
startStopButton.addActionListener(new ActionListener(){#Override public void actionPerformed(ActionEvent event){
if(startStopButton.getText()=="Start"){
// Start SwingWorker to perform whatever is supposed to happen here.
}
You can find some information on how to use SwingWorker here, should you need it.
I am having trouble moving this JLabel across this JPanel? I put the code below. Basically what is supposed to happen, is the JLabel called "guy" slowly moves to the right. The only problem is, that the JLabel isn't refreshing it just disappears after the first time I move it.
public class Window extends JFrame{
JPanel panel = new JPanel();
JLabel guy = new JLabel(new ImageIcon("guy.gif"));
int counterVariable = 1;
//Just the constructor that is called once to set up a frame.
Window(){
super("ThisIsAWindow");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(panel);
panel.setLayout(null);
}
//This method is called once and has a while loop to exectue what is inside.
//This is also where "counterVariable" starts at zero, then gradually
//goes up. The variable that goes up is suposed to move the JLabel "guy"...
public void drawWorld(){
while(true){
guy.setBounds(counterVariable,0,50,50);
panel.add(guy);
counterVarialbe++;
setVisible(true);
try{Thread.sleep(100)}catch(Exception e){}
}
}
Any thoughts as to why the JLabel is just disappearing instead of moving to the right after I change the variable "counterVariable".
-Thanks! :)
Your code is causing a long-running process to run on the Swing event thread which is preventing this thread from doing its necessary actions: paint the GUI and respond to user input. This will effectively put your entire GUI to sleep.
Issue & suggestions:
Never call Thread.sleep(...) on the Swing Event Dispatch Thread or EDT.
Never have a while (true) on the EDT.
Instead use a Swing Timer for all of this.
No need to keep adding the JLabel to the JPanel. Once added to the JPanel, it remains there.
Likewise, no need to keep calling setVisible(true) on the JLabel. Once visible, it remains visible.
Call repaint() on the container holding the moving JLabel after you've moved it to request that the container and its children be re-drawn.
e.g.,
public void drawWorld(){
guy.setBounds(counterVariable,0,50,50);
int timerDelay = 100;
new javax.swing.Timer(timerDelay, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
countVariable++;
guy.setBounds(counterVariable,0,50,50);
panel.repaint();
}
}).start;
}
caveat: code not compiled, run, or tested in any way
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.