Reputation: 2438
This utility application takes a screenshot of multiple monitors by pressing a button on a JFrame. The intended logic of this method is as follows:
Even with the final "return frame to visible" step commented out of the code, leaving me seeing no frame on the screen after execution, the frame is visible in the screenshot. I do not know why.
Using print statements, I have determined that the firing of the method to turn the JFrame invisible does run before taking the screenshot. I also attempted to use an if statement to check if the JFrame was visible before taking the screenshot, and the if statement was never triggered.
What is the solution to this?
public Image capture(ArrayList<Monitor> monitors) {
Rectangle bounds = new Rectangle();
monitors.stream().forEach(a -> Rectangle.union(bounds, a.getBounds(), bounds) );
Image image = null;
try {
EventQueue.invokeAndWait(() -> {
frame.setVisible(false);
System.out.println("Set frame invisible");
});
} catch (Exception ex) {
ex.printStackTrace();
}
try {
image = new Image(new Robot().createScreenCapture(bounds));
} catch (Exception ex) {
ex.printStackTrace();
}
//frame.setVisible(true);
return image;
}
Upvotes: 0
Views: 252
Reputation: 473
This is not a definitive answer, but maybe it'll point you in the right direction.
I had a similar problem requiring a JFrame to go fullscreen on a 3D application using OpenGL.
What I assume is happening is that frame.setVisible() talks to the OS window manager requesting the window be hidden (or shown). Once the request is made and acknowledged, the method call is no longer blocking, and the thread in invokeAndWait() is now done invoking and waiting.
Your code proceeds to a take a screenshot, but the operating system is not guaranteed to have actually processed the request to minimize the Window. In your case, it appears that is doesn't.
The Solution (maybe?): It looks like Java has a class called a WindowEvent doc here. You should be able to create a listener and/or a loop before the screenshot call that waits for a status update. Note the doc specifically says:
The window-deactivated event type. This event is delivered when the Window is no longer the active Window. Only a Frame or a Dialog can be the active Window. The native windowing system may denote the active Window or its children with special decorations, such as a highlighted title bar. The active Window is always either the focused Window, or the first Frame or Dialog that is an owner of the focused Window.
I suspect waiting for the WINDOW_DEACTIVATED and/or WINDOW_LOST_FOCUS could be the actual indicator of if the window has been minimized by the OS window manager.
Again, I'm basing this off of a mostly unrelated project I worked on several months ago, but hopefully some of this will help.
UPDATE
OP Implemented a Solution as follows:
public Image capture(ArrayList<Monitor> monitors)
{
Rectangle bounds = new Rectangle();
monitors.stream().forEach(a -> Rectangle.union(bounds, a.getBounds(), bounds) );
Image image = null;
try
{
EventQueue.invokeAndWait(() -> frame.setExtendedState(Frame.ICONIFIED));
while (frame.getExtendedState() != Frame.ICONIFIED) { }
image = new Image(new Robot().createScreenCapture(bounds));
}
catch (Exception ex)
{
ex.printStackTrace();
}
EventQueue.invokeLater(() -> frame.setExtendedState(Frame.NORMAL));
return image;
}
Upvotes: 1