Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
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 5 of 6

Figure 5a. Proxy begins loading image

Figure 5b. Proxy finishes loading image and fires a property change event

Figure 5c. Listener reacts to the property change and displays a dialog box
The application, shown in Figures 5a, 5b, and 5c, is listed in Example 5:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
import java.util.ArrayList;
// 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);
((ImageIconProxy)imageIconProxy).addPropertyChangeListener(
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
JOptionPane.showMessageDialog(VirtualProxyTest.this,"Image Loaded");
}
});
// 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
}
}
// 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...".
//
// This version of the ImageIconProxy class makes the real icon a bound
// property: when the property changes, ImageIconProxy fires a
// PropertyChangeEvent to all interested property change listeners.
class ImageIconProxy implements javax.swing.Icon {
private ArrayList propertyChangeListeners = new ArrayList();
private Icon realIcon = null;
private 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 realIcon == null ? height : realIcon.getIconHeight();
}
public int getIconWidth() {
return realIcon == null ? width : realIcon.getIconWidth();
}
// The proxy's paint method is overloaded to draw a border
// and a message ("Loading image...") while the image is
// loaded. After the image is 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 image is loaded via a Runnable passed to
// SwingUtilties.invokeLater(), which means that
// it is executed on another thread.
synchronized(this) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
// Slow the image-loading process.
Thread.currentThread().sleep(2000);
// ImageIcon constructor creates the image.
realIcon = new ImageIcon(imageName);
isIconCreated = true;
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
c.repaint();
notifyPropertyChangeListeners();
}
});
}
}
}
public void addPropertyChangeListener(
PropertyChangeListener listener) {
propertyChangeListeners.add(listener);
}
private void notifyPropertyChangeListeners() {
for(int i=0; i < propertyChangeListeners.size(); ++i) {
PropertyChangeListener listener = (PropertyChangeListener)
propertyChangeListeners.get(i);
listener.propertyChange(null);
}
}
}
As you can see from Example 5, it's not difficult to make an existing class a property change event source; at a minimum, you have to: