|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 6

Figure 1. A Swing icon
The application shown in Figure 1 is listed in Example 2:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
// This class tests an image icon.
public class IconTest extends JFrame {
private static String IMAGE_NAME = "mandrill.jpg";
private static int FRAME_X = 150, FRAME_Y = 200,
FRAME_WIDTH = 268, FRAME_HEIGHT = 286;
private Icon imageIcon = null, imageIconProxy = null;
static public void main(String args[]) {
IconTest app = new IconTest();
app.show();
}
public IconTest() {
super("Icon Test");
imageIcon = new ImageIcon(IMAGE_NAME);
setBounds(FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint(Graphics g) {
super.paint(g);
Insets insets = getInsets();
imageIcon.paintIcon(this, g, insets.left, insets.top);
}
}
The preceding application creates an image icon -- an instance of javax.swing.ImageIcon -- and then overrides the paint() method to paint the icon.
The application shown in Figure 1 is a poor use of Swing image icons because you should use image icons only for small pictures.
That restriction exists because creating images is expensive, and ImageIcon instances create their images when they are constructed. If an application creates many large images at once, it could cause
a significant performance hit. Also, if the application does not use all of its images, it's wasteful to create them upfront.
A better solution loads images as they become needed. To do so, a proxy can create the real icon the first time the proxy's
paintIcon() method is called. Figure 2 shows an application that contains an image icon (on the left) and an image-icon proxy (on the
right). The top picture shows the application just after its launch. Because image icons load their images when they are constructed,
an icon's image displays as soon as the application's window opens. In contrast, the proxy does not load its image until it
is painted for the first time. Until the image loads, the proxy draws a border around its perimeter and displays "Loading
image..." The bottom picture in Figure 2 shows the application after the proxy has loaded its image.


Figure 2. An icon (left) and an icon proxy (right)
I've listed the application shown in Figure 2 in Example 3:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
// This class tests a virtual proxy, which is a proxy that
// delays loading an expensive resource (an icon) until that
// resource is needed.
public class VirtualProxyTest extends JFrame {
private static String IMAGE_NAME = "mandrill.jpg";
private static int IMAGE_WIDTH = 256, IMAGE_HEIGHT = 256,
SPACING = 5, FRAME_X = 150,
FRAME_Y = 200, FRAME_WIDTH = 530,
FRAME_HEIGHT = 286;
private Icon imageIcon = null, imageIconProxy = null;
static public void main(String args[]) {
VirtualProxyTest app = new VirtualProxyTest();
app.show();
}
public VirtualProxyTest() {
super("Virtual Proxy Test");
// Create an image icon and an image-icon proxy.
imageIcon = new ImageIcon(IMAGE_NAME);
imageIconProxy = new ImageIconProxy(IMAGE_NAME,
IMAGE_WIDTH,
IMAGE_HEIGHT);
// Set the bounds of the frame, and the frame's default
// close operation.
setBounds(FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint(Graphics g) {
super.paint(g);
Insets insets = getInsets();
imageIcon.paintIcon(this, g, insets.left, insets.top);
imageIconProxy.paintIcon(this, g,
insets.left + IMAGE_WIDTH + SPACING, // width
insets.top); // height
}
}
Example 3 is nearly identical to Example 2, except for the addition of the image-icon proxy. The Example 3 application creates
the icon and the proxy in its constructor, and overrides its paint() method to paint them. Before discussing the proxy's implementation, look at Figure 3, which is a class diagram of the proxy's
real subject, the javax.swing.ImageIcon class.
Figure 3. ImageIcon's class diagram. Click on thumbnail to view full-size image.
The javax.swing.Icon interface, which defines the essence of Swing icons, includes three methods: paintIcon(), getIconWidth(), and getIconHeight(). The ImageIcon class implements the Icon interface, and adds methods of its own. Image icons also maintain a description of, and a reference to, their images.
Image-icon proxies implement the Icon interface and maintain a reference to an image icon -- the real subject -- as the class diagram in Figure 4 illustrates.
Figure 4. ImageIconProxy class diagram. Click on thumbnail to view full-size image.
The ImageIconProxy class is listed in Example 4.
// ImageIconProxy is a proxy (or surrogate) for an icon.
// The proxy delays loading the image until the first time the
// image is drawn. While the icon is loading its image, the
// proxy draws a border and the message "Loading image..."
class ImageIconProxy implements javax.swing.Icon {
private Icon realIcon = null;
boolean isIconCreated = false;
private String imageName;
private int width, height;
public ImageIconProxy(String imageName, int width, int height){
this.imageName = imageName;
this.width = width;
this.height = height;
}
public int getIconHeight() {
return isIconCreated ? height : realIcon.getIconHeight();
}
public int getIconWidth() {
return isIconCreated realIcon == null ? width : realIcon.getIconWidth();
}
// The proxy's paint() method is overloaded to draw a border
// and a message ("Loading image...") while the image
// loads. After the image has loaded, it is drawn. Notice
// that the proxy does not load the image until it is
// actually needed.
public void paintIcon(final Component c,
Graphics g, int x, int y) {
if(isIconCreated) {
realIcon.paintIcon(c, g, x, y);
}
else {
g.drawRect(x, y, width-1, height-1);
g.drawString("Loading image...", x+20, y+20);
// The icon is created (meaning the image is loaded)
// on another thread.
synchronized(this) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
// Slow down the image-loading process.
Thread.currentThread().sleep(2000);
// ImageIcon constructor creates the image.
realIcon = new ImageIcon(imageName);
isIconCreated = true;
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
// Repaint the icon's component after the
// icon has been created.
c.repaint();
}
});
}
}
}
}
ImageIconProxy maintains a reference to the real icon with the realIcon member variable. The first time the proxy is painted, the real icon is created on a separate thread to allow the rectangle
and string to be painted (the calls to g.drawRect() and g.drawString() do not take effect until the paintIcon() method returns). After the real icon is created, and therefore the image is loaded, the component that displays the icon
is repainted. Figure 5 shows a sequence diagram for those events.
Figure 5. Paint an image proxy. Click on thumbnail to view full-size image.
The Figure 5's sequence diagram is typical of all proxies: Proxies control access to their real subject. Because of that control, proxies often instantiate their real subject, as is the case for the image icon proxy listed in Example 4. That instantiation is one of the differences between the Proxy pattern and the Decorator pattern: Decorators rarely create their real subjects.