Is it possible to be notified whenever any window in the application was created or closed?
At the moment I'm polling Window.getWindows() but I would prefer to get notified instead.
What I have:
List<Window> previousWindows = new ArrayList<>();
while (true) {
List<Window> currentWindows = Arrays.asList(Window.getWindows());
for (Window window : currentWindows) {
if (!previousWindows.contains(window)) {
//window was created
}
}
for (Window window : previousWindows) {
if (!currentWindows.contains(window)) {
//window was closed
}
}
previousWindows = currentWindows;
Thread.sleep(1000);
}
What I'd like:
jvm.addWindowListener(this);
#Override
public void windowWasDisplayed(Window w) {
//window was created
}
#Override
public void windowWasClosed(Window w) {
//window was closed
}
You can register listeners that receive any subset of types of AWT events via the windowing Toolkit. From those you can select and handle the WindowEvents for windows being opened and closed, something like this:
class WindowMonitor implements AWTEventListener {
public void eventDispatched(AWTEvent event) {
switch (event.getID()){
case WindowEvent.WINDOW_OPENED:
doSomething();
break;
case WindowEvent.WINDOW_CLOSED:
doSomethingElse();
break;
}
}
// ...
}
class MyClass {
// alternative 1
public void registerListener() {
Toolkit.getDefaultToolkit().addAWTEventListener(new WindowMonitor(),
AWTEvent.WINDOW_EVENT_MASK);
}
// alternative 2
public void registerListener(Component component) {
component.getToolkit().addAWTEventListener(new WindowMonitor(),
AWTEvent.WINDOW_EVENT_MASK);
}
}
I would recommend alternative 2, where the Component from which you obtain the Toolkit is the main frame of your application (there should be only one), but alternative 1 should work out for you if you have to do this without reference to any particular component (for instance, before any have been created).
Do note, however, that registering an AWTEventListener is subject to a security check.
If you create the additional windows (I assume JFrames) yourself, you can use the addWindowListener method. The WindowAdapter abstract class allows you to override methods for the events you are interested in:
import java.awt.event.*;
import javax.swing.*;
public class MultipleWindows {
public static void main(String[] arguments) {
SwingUtilities.invokeLater(() -> new MultipleWindows().createAndShowGui());
}
private void createAndShowGui() {
JFrame frame = new JFrame("Stack Overflow");
frame.setBounds(100, 100, 800, 600);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.add(new JLabel("Testing multiple windows..."));
frame.getContentPane().add(panel);
WindowAdapter windowAdapter = new WindowAdapter() {
#Override
public void windowOpened(WindowEvent windowEvent) {
System.out.println("Window opened: "
+ windowEvent.getWindow().getName());
}
#Override
public void windowClosed(WindowEvent windowEvent) {
System.out.println("Window closed: "
+ windowEvent.getWindow().getName());
}
};
for (int windowIndex = 2; windowIndex < 6; windowIndex++) {
String title = "Window " + windowIndex;
JFrame extraFrame = new JFrame(title);
extraFrame.setName(title);
extraFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
extraFrame.addWindowListener(windowAdapter);
extraFrame.setVisible(true);
}
frame.setVisible(true);
}
}
Related
I have this code sample in a separate jDialog (jDialog is in the same package as that of JFrame) which used to check (using a Thread) if the jCheckBox1 in the jFrame is whether visible or not. JDialog is set to visible by clicking a JLabel (Change Password) in JFrame. I have not set the visibility of the JFrame even to false even after I click on the Change Password JLabel.
The problem I encountered is that even if the JFrame is not visible i.e when I run the JDialog separately (without clicking on the Change Password JLabel) it prints the "Visible" and I'm more than sure that the jFrame is not visible and not running.
This is the code snippet (Thread) I have used to check the visibility of the JFrame's jCheckBox1:
LockOptions lock = new LockOptions();
private void setLocation2() {
new Thread() {
public void run() {
boolean running = true;
while (running) {
try {
Thread.sleep(1000);
if (lock.jCheckBox1.isVisible()) {
System.out.println("Visible");
} else {
System.out.println("Not Visible");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
And this is the Code I have written in JFrame's Change Password JLabel:
private void jLabel9MouseClicked(java.awt.event.MouseEvent evt) {
Container c = new ChangePassword(this, rootPaneCheckingEnabled);
if (!c.isShowing()) {
c.setVisible(true);
hideMeToSystemTray();
this.requestFocusInWindow();
}
}
But when I run the JDialog separately (without clicking on the Change Password JLabel) it prints the "Visible"
I have attached a Screenshots of both JFrame and JDialog
JFrame containing jCheckBox1
JDialog:
OK, let's have the simplest possible example.
The following code creates a main frame having a button to create a new frame of class LockOptionsWindow, which extends JFrame.
The class FrameDemo implements Runnable. So can it be accessed on the event dispatching thread using SwingUtilities.invokeLater as mentioned in Swing's Threading Policy. So it is possible creating a new thread checklockoptionswindow which then can check whether the new window created by the button is visible or not visible.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FrameDemo extends WindowAdapter implements ActionListener, Runnable {
private LockOptionsWindow lockoptionswindow;
private Thread checklockoptionswindow = new Thread();
private void showLockOptionsWindow() {
if (lockoptionswindow != null && lockoptionswindow.isDisplayable()) {
lockoptionswindow.setVisible(true);
lockoptionswindow.setExtendedState(Frame.NORMAL);
} else {
lockoptionswindow = new LockOptionsWindow();
lockoptionswindow.setSize(new Dimension(300, 100));
lockoptionswindow.setVisible(true);
lockoptionswindow.setExtendedState(Frame.NORMAL);
}
}
private void startCheckLockOptionsWindow() {
if (!checklockoptionswindow.isAlive()) {
checklockoptionswindow = new Thread() {
public void run() {
boolean running = true;
while (running) {
try {
Thread.sleep(1000);
if (lockoptionswindow.isVisible()) {
if (lockoptionswindow.getExtendedState() == Frame.ICONIFIED) {
System.out.println("Visible iconified");
} else {
System.out.print("Visible on screen ");
int x = lockoptionswindow.getLocation().x;
int y = lockoptionswindow.getLocation().y;
System.out.println("at position " + x + ", " + y);
}
} else {
System.out.println("Not Visible");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
checklockoptionswindow.start();
}
}
public void actionPerformed(ActionEvent e) {
showLockOptionsWindow();
startCheckLockOptionsWindow();
}
public void run() {
JFrame frame = new JFrame("FrameDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Show LockOptions frame");
button.addActionListener(this);
Container contentPane = frame.getContentPane();
contentPane.add(button);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new FrameDemo());
}
class LockOptionsWindow extends JFrame {
public LockOptionsWindow() {
super("LockOptions frame");
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
}
}
}
Edited to determine whether the LockOptionsWindow is visible iconified only or is really showed as window on the screen.
I am trying to write application which will create new window when pressing button, show current window count and close window and thread on closing the window.
So basically, functionality is like this (and some of them are working):
Show window on launching application (OK)
Create new window on pressing button (OK)
Display current window count on pressing button (OK on creating, NOK on closing windows)
Destroy window when pressed "X" (OK)
Destroy main thread when original window is closed (NOK)
Here is my code:
package projectpackage;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JOptionPane;
import java.awt.*;
import java.awt.event.*;
class MyMouseEvent extends MouseAdapter
{
public void mousePressed(MouseEvent me)
{
MyWindowThread.incrementMyWindowThreadCount();
MyWindowThread obj1 = new MyWindowThread();
Thread thr1 = new Thread(obj1);
thr1.start();
}
}
class MyWindowThread extends JFrame implements Runnable
{
private int height;
private int width;
private static int MyWindowThreadCount = 1;
public static int getMyWindowThreadCount()
{
return MyWindowThreadCount;
}
public static void incrementMyWindowThreadCount()
{
MyWindowThreadCount++;
}
public static void decrementMyWindowThreadCount()
{
MyWindowThreadCount--;
}
public MyWindowThread()
{
super("Frame "+getMyWindowThreadCount());
this.setHeight(300);
this.setWidth(400);
this.setBounds(100,100,getWidth(),getHeight());
this.addWindowListener(new WindowAdapter()
{
#Override
public void windowClosing(WindowEvent e)
{
MyWindowThread.decrementMyWindowThreadCount();
//Thread.currentThread().interrupt();
return;
}
}
);
JPanel panel1 = new JPanel();
JButton button1 = new JButton("New window");
JButton button2 = new JButton("Count running windows");
this.getContentPane().add(panel1);
panel1.add(button1);
panel1.add(button2);
button1.addMouseListener(new MyMouseEvent());
button2.addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent me)
{
javax.swing.JOptionPane.showMessageDialog(null,"Window count = " + MyWindowThread.getMyWindowThreadCount());
}
}
);
this.setVisible(true); // show frame
}
public int getHeight()
{
return this.height;
}
public void setHeight(int h)
{
this.height = h;
}
public int getWidth()
{
return this.width;
}
public void setWidth(int w)
{
this.width = w;
}
public void run()
{
}
}
public class MyApp
{
public static void main(String[] args)
{
// Creating objects START
MyWindowThread obj1 = new MyWindowThread();
Thread thr1 = new Thread(obj1);
thr1.start();
}
}
manifest.mf:
Manifest-Version: 1.0
Main-Class: projectpackage.MyApp
I hope you know how to compile and run Java application in console. In case you don't, here it is:
(navigate in CLI to directory with "projectpackage" directory inside. *.java file must be inside "projectpackage" directory)
javac projectpackage\*.java
jar cfm MyApp.jar manifest.mf projectpackage\*
java -jar MyApp.jar
Can anyone enlighten me what am I doing wrong or just what I need to do to make the code work? Or am I just having fundamental misconception?
JFrame frame = new JFrame();
This is your problem, you extend the JFrame-class but try to use a new object.
You should replace this line by super(); or super("Frame " + getMyThreadCount()); if you want so.
Edit: Another issue to mention: you named your class MyThread, this is a quite irritating class-name for a window ;)
Edit 2: Few other things:
public void run() has no content, so why does MyThread implement Runnable?
You create some threads, but if I got that right, you do not start any of them.
If you simply want your application to exit when the JFrame is closed, then you are going about this all wrong. Get rid of all the Thread objects and Runnables. Swing applications run in the event dispatch thread (EDT). You should only use background threads if you need to do something like disk or network I/O.
Create/show the JFrame in the EDT. If you set the default close operation on the JFrame to DISPOSE_ON_CLOSE, then when all of the windows have been closed, the main thread will terminate normally.
public class MyApp {
public static void main( String[] args ) {
EventQueue.invokeLater( new Runnable() {
public void run() {
MyWindow w = new MyWindow();
w.setVisible( true );
}
} );
}
private static class MyWindow extends JFrame {
// Simplified reference counting
private static AtomicInteger frameCount = new AtomicInteger( 0 );
public MyWindow() {
super("Frame " + frameCount.incrementAndGet() );
this.setSize( 400, 300 );
this.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
JButton button = new JButton("New Window");
button.addActionListener( new ActionListener() {
#Override
public void actionPerformed( ActionEvent e ) {
MyWindow w = new MyWindow();
w.setVisible( true );
}
} );
this.getContentPane().add( button );
// ignore reference count button for now
}
}
}
There is a class AgentHome which extends JFrame.
AgentHome has a JPanel rem_panel. Checkboxes are added dynamically into rem_panel…number of checkboxes depending on the number of entries in the database table from where the text to be displayed by the textboxes are read.
AgentHome has an integer variable x and a checkbox arraylist rem_cbarr.
rem_cbarr stores the checkboxes as they are created and added to rem_panel.
I am trying to set the background color of these checkboxes to red when the variable x is set to 1 as the program executes.
I have implemented the TickerBehaviour of JADE framework to check if the variable x is set to 1.
I am unable to set the background color of the checkboxes to red. This is the code I have implemented. Please help. Thanks.
public void setup()
{
Behaviour loop = new TickerBehaviour( this, 2000 )
{
protected void onTick() {
timer();
}
};
addBehaviour( loop );
}
public void timer()
{
AgentHome hm=new AgentHome();
if(hm.x==1)
{
for (int i = hm.rem_cbarr.size()-1; i>=0; i--)
{
JCheckBox cb=hm.rem_cbarr.get(i);
cb.setBackground(Color.red);
hm.rem_panel.revalidate();
hm.rem_panel.repaint();
}
}
}
GUI operations need to be done on the EDT (Event Dispatcher Thread). In java this happens by calling SwingUtilities.invokeLater(Runnable run).
A number of things...
UI components should only ever be updated within the context of the Event Dispatching Thread
You should never perform any action which might block the Event Dispatching Thread (like using loops or Thread#Sleep to try and update the screen)
The Event Dispatching Thread is responsible for dispatching paint updates...
JCheckBox is transparent by default.
public class FlashCheckBox {
public static void main(String[] args) {
new FlashCheckBox();
}
public FlashCheckBox() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(new FlashyCheckBox());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class FlashyCheckBox extends JCheckBox {
private final Color defaultBackground;
private int flash;
private Timer flashTimer;
public FlashyCheckBox() {
defaultBackground = getBackground();
flashTimer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
flash++;
if (flash % 5 == 0) {
setOpaque(false);
setBackground(defaultBackground);
flashTimer.stop();
} else if (flash % 2 == 0) {
setBackground(Color.YELLOW);
setOpaque(true);
} else {
setBackground(defaultBackground);
setOpaque(false);
}
repaint();
}
});
flashTimer.setRepeats(true);
flashTimer.setCoalesce(true);
flashTimer.setInitialDelay(0);
addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
flashTimer.restart();
}
});
}
}
}
I am just wondering... How to block main thread as JOptionPane (its confirmation dialog) does to be able hold execution until it returns some value? I've never done it before so I am interested to find an optimal way...
to be more clear I am interested to express the conception as something like this but I am not sure how to make it wait until an event happens in popupmenu for example:
...
int a;
int x;
void showPopup()
{
a=MyPopupMenu.aPopupMenu();
if(a==0){System.out.println("YES");}
if(a==1){System.out.println("NO"); return;}
x++;
}
Thanks
Use wait/notify. The main thread will do:
synchronized(object) {
object.wait(timeOut);
}
and the handler (ie listener) for whatever GUI you are using (a popup in your example) will do:
synchronized(object) {
object.notify();
}
Here is a stripped-down instructional example using wait/notify
public class ModalPopUp {
JPopupMenu popUpMenu;
JMenuItem menuItem;
static Object modalMonitor = new Object();
public void popIt(Component parent, int x, int y) {
popUpMenu.show(parent, x, y);
}
public void stopIt() {
popUpMenu.setVisible(false);
}
public ModalPopUp() {
popUpMenu = new JPopupMenu();
menuItem = new JMenuItem("Click me to Continue");
popUpMenu.add(menuItem);
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
synchronized (modalMonitor) {
modalMonitor.notify();
}
}
});
}
public static void main(String args[]) throws Exception {
ModalPopUp p = new ModalPopUp();
JFrame frame = new JFrame();
frame.setSize(400, 400);
frame.setVisible(true);
p.popIt(null, 100, 100);
System.out.println("Waiting upto 42s for popup click");
synchronized (modalMonitor) {
modalMonitor.wait(42000);
}
p.stopIt();
System.out.println("Popup was clicked or 42s passed");
}
}
Instead of using the static JOptionPane methods you can use an instance of a JDialog directly, allowing you to customize its behavior, such as the modality.
How to Make Dialogs
How can i exit only they new MainGame that i created from Main?
Where Main is having an original layer of game. And the MainGame was a dialog window (such as modal windows).
Main.java: (main code)
public class Main extends JWindow
{
private static JWindow j;
public static MainGame mp;
public static void main(String[] args)
{
new Thread(new Runnable()
{
public void run()
{
mp = new MainGame();
mp.runit();
//mp.stopit();
}
}).start();
j = new Main();
j.setVisible(true);
}
}
MainGame.java: (this was extended by Main, and i would like to quite this only).
public class MainGame extends JWindow
{
private static JWindow j;
public MainGame()
{
// some GUI ...
}
public static void runit()
{
j = new MainGame();
j.setVisible();
}
}
1) better would be implements CardLayout, as create Top-Level Container for new Window, then you'll only to switch betweens Cards
2) don't create lots of Top-Level Container on Runtime, because there are still in JVM memory untill current instance exist,
create required number of and re-use that, to avoiding possible memory lacks
then you have to call setVisible(false) and setVisible(true)
JWindow missed methods for setting setDefaultCloseOperation(Whatever);
3) if you'll create constructor public JWindow(Frame owner), then you'll call directly
SwingUtilities.getAccessibleChildrenCount() and SwingUtilities.getWindowAncestor()
import javax.swing.*;
import java.awt.*;
public class Testing {
private JFrame f = new JFrame("Main Frame");
private JWindow splashScreen = new JWindow();
public Testing() {
splashScreen = new JWindow(f);
splashScreen.getContentPane().setLayout(new GridBagLayout());
JLabel label = new JLabel("Splash Screen");
label.setFont(label.getFont().deriveFont(96f));
splashScreen.getContentPane().add(label, new GridBagConstraints());
splashScreen.pack();
splashScreen.setLocationRelativeTo(null);
splashScreen.setVisible(true);
new Thread(new Runnable() {
#Override
public void run() {
readDatabase();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}).start();
}
public void readDatabase() {
//simulate time to read/load data - 10 seconds?
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
public void createAndShowGUI() {
JLabel label = new JLabel("My Frame");
label.setFont(label.getFont().deriveFont(96f));
f.add(label);
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
System.out.println("JFrame getAccessibleChildrenCount count -> "
+ SwingUtilities.getAccessibleChildrenCount(f));
System.out.println("JWindow getParent -> "
+ SwingUtilities.getWindowAncestor(splashScreen));
splashScreen.dispose();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Testing t = new Testing();
}
});
}
}
I did not go really into your design. but there is 'j.dispose();'.
this should work. here is the java documentation.
notice:
dispose(); - deletes the window from memory.
setVisibilty(false); - just hides it from the screen.
You can override the 'dispose()' function to do some stuff while the widow is closing (updating scores if its a game) but at the end of the overriden function you have to call 'super.dispose();' so the function of the class Window is called.
And the MainGame was a dialog window
But thats not what your code uses. You use a JWindow.
You should be using a JDialog for a modal window. Then you just dispose() the window.