When designing a Java applet or application, you often may wish to place a similar set of components in several different places. Perhaps you would like to have a labeled text entry field, or a more complicated structure like a list of IP addresses and subnet masks, with entry fields for each. You could do these by creating a Panel, placing them there, having a method in your class to create this, and returning the Panel. There is an easier way.
A 'macro' component [treating multiple components as a single object] is a simple step between writing a custom component which handles all the events and drawing its needs, and just creating a new panel in your existing class. From an object-oriented standpoint, it is a cleaner, simpler way to write code. From a Java Abstract Window Toolkit (AWT) standpoint, it is simpler than trying to deal with all the Events and data within a single class.
As an example, consider a labeled choice. The AWT does not contain this construct, but it's likely something you use a lot in your applets. Perhaps you have created a method 'createLabelledChoiceComponent', and it returns a panel which you add to yourself. You could just as easily create a 'labelledChoice' class. For this trivial example, you would need to provide a constructor, taking the label string as a parameter. Additionally, you could provide methods to get at the Choice component methods: addItem(), getItem(), etc. Another way you could do this would be to provide a getChoice() method, simply returning the Choice to the caller.
You may be wondering why this example extends Panel, instead of Choice. If it extended Choice, you would automatically have access to all the Choice methods. The problem is, I need this to be a layout manager in order to hold the two components, the Label and the Choice.
Say you wished to create a list of IP addresses. You would like to prevent the user from entering something that wasn't an IP address in it. Additionally, you would like to group this list, somehow, so that it stood out as a unit.
- In the example above, there is no benefit to having placed this code in a separate class. It would have been just as simple to create a Panel and parent the Label and the Choice directly on it. The benefit becomes clear, however, when there is a need to do event handling.
In the next example, I have created a new class which presents the user with a labeled box for entering IP addresses. Drawing a box around it was easy since it is a component of its own and has bounds(). In addition, I don't allow any keys other than editing keys and '0-9' plus '.' in the IP address field. The event handler discards any other key. The outcome of this is shown in Figure 1.
If you examine the source code in Listing 2, you will see how easy it was to accomplish this entire component. The border around it was obtained by overriding the paint function to always draw a box of our bounding size. The key-filtering in the IP field was accomplished by passing only those keys which we wanted. Recall that if an event handler returns 'true', then it has handled the event, and the next event handler won't see it. Thus, I return 'true' if it is a key I don't want.
The benefits of making this list into a component are much clearer than the first example:
This simple (and obvious) technique can make your coding simpler, provide you with a library of reusable components for your other applets, and make documentation simpler.
- It was trivial to draw a border around it.
- Event handling was simple -- It didn't conflict with the rest of the applet.
- We can now create and add multiple lists of this type trivially.
- There is a single piece of code to change if we want additional functionality.
- There is a possibility we can share this component with other applets.
About the Author
Don Bowman is a software designer with Hewlett-Packard. He is the co-webmaster for his division. Don has worked with Java on HP-UX and Windows 95 environments.
Listing 1: Labeled choice method
labelledChoice extends Panel
label = new Label(l);
choice = new Choice();
Listing 2: IPList method
class IPList extends Panel
listLabel = new Label(label);
0, 0, 1, 1,
1, 1, 1, 1);
Panel ipPanel = new Panel();
Label ipLabel = new Label("IP");
ipTextField = new TextField(15);
0, 1, 1, 1,
1, 1, 1, 10);
addButton = new Button("Add");
1, 1, 1, 1,
1, 1, 1, 5);
addressList = new List();
0, 2, 1, 5,
1, 10, 10, 10);
removeButton = new Button("Remove");
1, 2, 1, 1,
1, 1, 1, 5);
constrain( Component component,
int grid_x, int grid_y,
int grid_width, int grid_height,
double weight_x, double weight_y,
int top, int left,
int bottom, int right)
GridBagConstraints cst = new GridBagConstraints();
cst.gridx = grid_x;
cst.gridy = grid_y;
cst.gridwidth = grid_width;
cst.gridheight = grid_height;
cst.fill = fill;
cst.anchor = anchor;
cst.weightx = weight_x;
cst.weighty = weight_y;
cst.insets = new Insets(top, left, bottom, right);
int sel = addressList.getSelectedIndexes();
for (int i = 0; i < sel.length; i++)
String ip = ipTextField.getText();
String ip = addressList.getSelectedItem();
if (event.target == addButton)
else if (event.target == removeButton)
if (event.target == ipTextField)
case '0': case '1': case '2':
case '3': case '4': case '5':
case '6': case '7': case '8':
Rectangle r = bounds();
g.drawRect(0, 0, r.width-1, r.height-1);