Posted by Rob Camick on October 13, 2008
Do you need the ability to create an image of your GUI or a component on your GUI? The easiest way to do this would be to use the Robot class and its createScreenCapture(…) method. This will work whether you have an AWT or Swing application. However, this method is relatively slow as it gets the pixels directly from the screen. There must be a better way.
The better way is to use the Swing JComponent.paint(…) method to draw the Swing component directly onto a BufferedImage. Of course this can only be used on Swing components. This may lead to some confusion because although JFrame, JDialog and JWindow are part of Swing, they actually extend from Component, not JComponent, which means you can’t use the paint(…) method and must use the Robot.
No reason to worry though because the ScreenImage class will take the above into account when creating an image. There are various static methods that can be used to create images:
- createImage(JComponent) – create an image of the specified Swing component.
- createImage(JComponent, Rectangle) – create an image of the specified Rectangle of a Swing component.
- createImage(Component) – create an image of the specified component. JFrame and JDialog (for example) extend from Component, not JComponent.
- createDestopImage() – is a convenience method which will create an image of the whole desktop.
- createImage(Rectangle) – create an image of a specified Rectangle of the desktop.
In general all you need to provide is a reference to the component you want to create an image for. However, if you don’t want the entire component you can specify a region of that component. This is important to understand when creating images for components in a scroll pane. For example, lets say you have a text area with 1,000 lines in scroll pane. You really have 3 ways to create the image:
- use the JTextArea as the component and all 1,000 lines will be painted, even though they are not visible on the screen
- use the JTextArea as the component but specify a region so that only a certain number of lines are painted
- use the JViewport as the component and only the visible lines will be painted.
This class also contains a static writeImage(…) method that allows you to save the created image to a file. The type of file must be supported by the ImageIO write method.
Download the code and check out the API, I’m sure you’ll be using it in no time. I used the following code to create the attached image showing a portion of my desktop:
Rectangle r = new Rectangle(375, 50, 600, 300);
BufferedImage bi = ScreenImage.createImage(r);
Swing components not added to a GUI
Although this class was originally designed to create an image of a component on the screen it can be used to create an image of Swing components not displayed on a GUI. Behind the scenes the ScreenImage class will:
- set the component size – when the size of a component is 0, the size will be set to the preferred size of the component. Or, you can manually set the size of the component before invoking a createImage(…) method
- layout the component – components like JPanel, JComboBox, JScrollPane need to be layed out before then can be painted. This class will recursively invoke the doLayout() method on the Containers of the component.
Even with the above features, there is still a possiblity the components image will not be what you expect. For example the image of a table added to a scroll pane will be missing the table header. This is because the header is not added to the scroll pane until the scroll pane has been added to a realized GUI window. That is the window must be packed or made visible. A simple work around for this problem is:
JScrollPane scrollPane = new JScrollPane( table );
scrollPane.setColumnHeaderView( table.getTableHeader() );
ScreenImage.createImage( scrollPane );
If you still encounter problems then the only other suggestion I have is to add the component to a realized frame:
JFrame frame = new JFrame();
frame.setContentPane( someComponent );
ScreenImage.createImage( someComponent );
Thanks to Maxideon for his comments in this posting which lead to improvements in the creation of images for non displayed components.