testing a game, i sometimes get exceptions thrown when a component is not displayable. i added a wait loop on isDisplayable().
seems like my game can take a few hundred ms. to become displayable.
is this a sane way to handle this problem?
i am testing game clients that talk over sockets to a server.
thanks
edit 1: thanks for the comments. i discovered that i am adding the mediator for the gui (an observer) to the model (an observable) before the gui completes its construction and initialization. it gets worse, as i am initializing a panel with calls to createImage which returns null and throws.
import java.awt.*;
import javax.swing.*;
public class WaitForFrameToBeVisible {
JFrame frame=new JFrame("FrameDemo");
private void createAndShowGUI() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel emptyLabel=new JLabel("");
emptyLabel.setPreferredSize(new Dimension(175,100));
frame.getContentPane().add(emptyLabel,BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
void run() throws Exception {
System.out.println("waiting for frame to be displayable");
long t0=System.nanoTime();
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
while (!frame.isDisplayable())
Thread.sleep(1);
long dt=System.nanoTime()-t0;
System.out.println("waited "+dt/1_000_000.+" ms. for frame to be displayable");
}
public static void main(String[] args) throws Exception {
new WaitForFrameToBeVisible().run();
}
}
You don't need to use isDisplayable(). You could use invokeAndWait(...) for what you are trying to do :
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createAndShowGUI();
}
});
// control will reach here when the GUI will be created
Notice that this way you are not actually checking if the frame is visible but you are checking if createAndShowGUI() has done its work
Another thing is you should not call isDisplayable() or any function like this in loop. It will consume unnecessary processing speed. Instead use wait-notify.
But as far as your case is concerned to use WindowListener is very good idea as suggested by MadProgrammer in comment.
Related
I understand that EventQueue.invokeLater() is a function called to make the Java Swing components Thread-Safe.
Also, I know that the argument to this function is an object with implements Runnable.
However, I am unable to understand the syntax for this function call, i.e. this call -
EventQueue.invokeLater(()-> {
new Screen();
});
Here, Screen() is a class that extends JFrame.
public class Screen extends JFrame
{
Screen()
{
setSize(1000, 1000);
JPanel j1 = new Board();
j1.setBounds(0,0,500, 500);
JPanel j2 = new DiceModel();
j2.setBounds(500, 0, 500, 500);
add(j1);
add(j2);
setLayout(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args)
{
EventQueue.invokeLater(()-> {
new Screen();
});
}
}
This code runs as expected.
Board and DiceModel are two classes I have defined that which extend JPanel.
The invocation
EventQueue.invokeLater( new Screen() );
gives the expected error that Screen is not an object of type Runnable.
So,my question is, what is the meaning of the syntax for the function call for invokeLater() ?
Is it a kind of anonymous function call in Java ?
The complete Swing processing is done in a thread called EDT (Event Dispatching Thread). Therefore you would block the GUI if you would compute some long lasting calculations within this thread.
The way to go here is to process your calculation within a different thread, so your GUI stays responsive. At the end you want to update your GUI, which have to be done within the EDT. Now EventQueue.invokeLater comes into play. It posts an event (your Runnable) at the end of Swings event list and is processed after all previous GUI events are processed.
Also the usage of EventQueue.invokeAndWait is possible here. The difference is, that your calculation thread blocks until your GUI is updated. So it is obvious that this must not be used from the EDT.
Still there is Java code out there that starts a JFrame simple from the main thread. This could cause issues, but is not prevented from Swing. Most modern IDEs now create something like this to start the GUI
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewJFrame().setVisible(true);
}
});
}
I've got a short question and I hope somebody can help me.
Please look at the following code snippet:
public void mouseEntered(MouseEvent e){
//wait 2 seconds.
//if no other mouseEntered-event occurs, execute the following line
//otherwise restart, counting the 2 seconds.
foo();
}
Can somebody help me with that problem? I want to realize a behavior like an ToolTip: you enter a region with your mouse. If your mouse stays in that position, do something.
Start a Timer with a delay of 2 seconds in your mouseEntered() method that calls whatever it is you want to do.
Set up a new handler (mouseExited()) that stops the timer if it hasn't gone off.
Basically, you know the mouse is still there if mouseExited() hasn't been called. The timer will either go off in two seconds doing what you want or be cancelled if the mouse exits.
Although the answer provided by #Brian Roach is correct, there is yet another (and more succinct) way of achieving this. That is, using the ToolTipManager.
Example:
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
public final class ToolTipDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
ToolTipManager.sharedInstance().setInitialDelay(2000);
createAndShowGUI();
}
});
}
private static void createAndShowGUI(){
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(new JToolTipButton());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static final class JToolTipButton extends JButton{
private static final long serialVersionUID = -5193366265809801639L;
protected JToolTipButton(){
super("I can haz tooltip?");
setToolTipText("Hey man, get off me!");
}
}
}
By invoking setInitialDelay, I've changed the time the manager waits to display the tool tip from 750ms to 2000ms.
Note - Although I'm not sure, I think this may change the delay for ALL components (guess I was right), which may not be what you want..but it's still worth mentioning.
Is there a way to send the java frame in front of every other opened program. I know you can use
JFrame.setAlwaysOnTop(true);
but that just keeps it in front allways. I want it to only happen when a certain function is called. For instance, When I press a button on a frame, it will wait using Thread.sleep(10000) for ten seconds, but the I want it to just the frame to the front in case you clicked out of the window for a second. Any suggestions?
Take a look at Window#toFront
You may also want to take a look at
WindowListener
Swing Timer
Be careful of using Thread.sleep in a GUI environment, if used incorrectly, this will cause you window to stop updating (painting)
This is surprisingly fiddly.
The exact behavior might also depend on the operating system. But at least on Windows, a call to frame.toFront() will not necessarily bring the window to the front. Instead, it will cause the corresponding entry in the task bar to blink for a few seconds. I tried something like
f.setAlwaysOnTop(true);
f.setAlwaysOnTop(false);
which basically works, but after the window was brought to the front, it is not "active", and none of my attempts to make it active worked (e.g. requesting the focus or so).
The only solution that I found now to (reliably) work (on Windows, at least) was
if (!f.isActive())
{
f.setState(JFrame.ICONIFIED);
f.setState(JFrame.NORMAL);
}
But wonder wheter there is a more elegant solution.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class FrameToTopTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
final JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Bring me to top after 3 seconds");
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
triggerBringToFront(f, 3000);
}
});
f.getContentPane().add(button);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static void triggerBringToFront(final JFrame f, final int delayMS)
{
Timer timer = new Timer(delayMS, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
// This will only cause the task bar entry
// for this frame to blink
//f.toFront();
// This will bring the window to the front,
// but not make it the "active" one
//f.setAlwaysOnTop(true);
//f.setAlwaysOnTop(false);
if (!f.isActive())
{
f.setState(JFrame.ICONIFIED);
f.setState(JFrame.NORMAL);
}
}
});
timer.setRepeats(false);
timer.start();
}
}
What is the best practice way to start a java swing application? Maybe there is another way to do it.
I want to know if i have to use the SwingUtilities class to start the application (secound possibility) or not (first possibility).
public class MyFrame extends JFrame {
public void createAndShowGUI() {
this.setSize(300, 300);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
// add components and stuff
this.setVisible(true);
}
public static void main(String[] args) {
// First possibility
MyFrame mf = new MyFrame();
mf.createAndShowGUI();
// Secound possibility
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MyFrame mf = new MyFrame();
mf.createAndShowGUI();
}
});
}
}
Only the second way is correct. Swing components must be created and accessed only in the event dispatch thread. See concurrency in swing. The relevant quote:
Why does not the initial thread simply create the GUI itself? Because almost all code that creates or interacts with Swing components must run on the event dispatch thread. This restriction is discussed further in the next section.
So yes, you need to use invokeLater().
import javax.swing.*;
public class Main
{
public Main()
{
JFrame jf = new JFrame("Demo");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(100, 100);
jf.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new Main();
}
});
Runtime.getRuntime().gc();
}
}
I call Runtime.getRuntime().gc(); for explicit garbage collector invoking. But window doesn't dissapear from screen, why doesn't garbage collector reclaim JFrame's object?
When a JFrame is created, it registers itself in some internal Swing data structures which allow it to receive events like mouse clicks. This means there is a reference to your object lurking somewhere until you tell Swing to get rid of the window using dispose().
Given the invokeLater() call, the call to GC will probably occur 1st1.
BTW - calling Runtime.gc() is generally pointless, the JRE won't GC till it needs to.
E.G.
Output
GC called
Frame visible
Code
package test;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class VisibleFrameGC {
VisibleFrameGC() {
JFrame jf = new JFrame("Demo");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(100, 100);
jf.setVisible(true);
System.out.println("Frame visible");
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new VisibleFrameGC();
}
});
Runtime.getRuntime().gc();
System.out.println("GC called");
}
}
The frame is visible and the reference to the object is reachable by at least one of the GUI threads (the Event Dispatch Thread). That is why it isn't garbage collected.
If you want it to disapear, use frame.dispose().