Reputation: 103
I want to take a print screen of what is behind the JFrame, without the JFrame itself. The way I tried was to close the window, wait for a while so it fully closes, then take the print screen and then open it again. The current method is not good for what I need since I need to do it more frequently and waiting for the window to close is too much time. Also, I don't want to close the window for that.
Here is my current code:
public class Offscreen {
private static BufferedImage img;
public static void main(String[] main)
throws AWTException, InterruptedException {
JFrame frame = new JFrame() {
@Override
public void paint(Graphics g) {
if (img != null)
// draw print screen
g.drawImage(img, 0, 0, null);
}
};
frame.setSize(500, 500);
frame.setLocation(700, 0);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setVisible(false);
Thread.sleep(200);
// take the picture
Robot r = new Robot();
img = r.createScreenCapture(frame.getBounds());
// show the source again
frame.setVisible(true);
// print on destiny
frame.repaint();
}
}
Upvotes: 0
Views: 1166
Reputation: 347234
"Assuming" you only want to capture the area behind the active frame (and as you say, without the frame), then something like..
import java.awt.AWTException;
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class PrintScreen01 {
public static void main(String[] args) {
new PrintScreen01();
}
private Timer timer;
public PrintScreen01() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
final PrintPane printPane = new PrintPane();
final JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(printPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.addComponentListener(new ComponentAdapter() {
@Override
public void componentMoved(ComponentEvent e) {
timer.restart();
}
@Override
public void componentResized(ComponentEvent e) {
timer.restart();
}
});
timer = new Timer(250, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (frame.isVisible()) {
takeSnapShot(frame, printPane);
}
}
});
timer.setRepeats(false);
frame.setVisible(true);
// takeSnapShot(frame, printPane);
}
});
}
public void takeSnapShot(JFrame frame, PrintPane pane) {
Rectangle bounds = new Rectangle(pane.getLocationOnScreen(), pane.getSize());
frame.setVisible(false);
new SnapShotWorker(frame, pane, bounds).execute();
}
public class SnapShotWorker extends SwingWorker<BufferedImage, BufferedImage> {
private JFrame frame;
private PrintPane pane;
private Rectangle captureBounds;
public SnapShotWorker(JFrame frame, PrintPane pane, Rectangle bounds) {
this.frame = frame;
this.pane = pane;
captureBounds = bounds;
}
@Override
protected BufferedImage doInBackground() throws Exception {
Thread.sleep(125);
BufferedImage snapShot = null;
try {
Robot bot = new Robot();
snapShot = bot.createScreenCapture(captureBounds);
} catch (AWTException ex) {
ex.printStackTrace();
}
Thread.sleep(125);
return snapShot;
}
@Override
protected void done() {
try {
BufferedImage snapShot = get();
pane.setSnapShot(snapShot);
frame.setVisible(true);
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (ExecutionException ex) {
ex.printStackTrace();
}
}
}
public class PrintPane extends JPanel {
private BufferedImage background;
public PrintPane() {
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void setSnapShot(BufferedImage img) {
background = img;
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.drawImage(background, 0, 0, this);
g2d.dispose();
}
}
}
}
May help...
Now, I had some issues with the timing of Robot
, it would seem that part of the operation is threaded in some way, meaning that unless you got the timing of the frame changing between invisible and visible "just" right, you would actually recapture the frame...annoying...
To try and fix this, and reduce the amount of times I tried capturing the screen, I employed a javax.swing.Timer
(to reduce the capture attempts) and a SwingWorker
to control the actual snapshot process, taking it out of the Event Dispatching Thread, there by ensuring (to some degree) that the window will be invisible.
I also added some additional lag to ensure that the frame was off the screen long enough to prevent it been capture by the snapshot...
Upvotes: 2