Building Reusable Java Widgets

An introduction to writing pluggable do-it-yourself widgets for the Java programmer.
1.3. Exercises for the Reader

There are a few interesting extensions you could make to this class, and I leave them to you:

  • Generic separator: To create a horizontal separator you might want to create a HorizontalSeparator class. But why not think about creating a single class that does both depending on how it is placed? It would need to know if it were placed in a vertical or horizontal region and render itself appropriately.

  • Additional features: The separator we created has a fixed width of 2 pixels and is etched. Change or extend your class to support a variable width and the option of etched, raised or flat.

Example 2. E-mail Entry Widget

The e-mail entry widget represents a simple entry form used to gather information from a user. It is a composite widget built with standard AWT components. This example widget is important in the way that it interacts with other classes. This class broadcasts customized events which can be captured by any interested object in the system. The ability to broadcast custom events to listeners of those events is critical in creating a reusable widget. The e-mail widget will have a done and cancel button. Remember, you do not want other objects to have direct access to these buttons or the events that they generate. Why? Suppose the e-mail widget changes down the line and no longer uses a button click to signify completion. Every object that uses the class will break and will have to be rewritten. If you hide these events, how can your widget notify interested parties of its changes in state? You must generate widget specific events and create listeners of these events. These events must make semantic sense to the action of the e-mail widget. We will see how this is done in a moment, but first let's create its visual appearance.

2.1. Layout

The visual layout of the e-mail entry form is created in its constructor. The widget consists of a text field, a label and two buttons.

Class EmailEntry extends Panel implements
        ActionListener {
public EmailEntry() {
        super() ;
        _doneButton = new Button( "Done" );
        _cancelButton = new Button( "Cancel" ) ;
        _emailField = new TextField( 40 ) ;
        // build a sub-panel for the buttons.
        Panel = new Panel() ;
        buttonPanel.add( _doneButton ) ;
        buttonPanel.add( _cancelButton ) ;
        // install the components in the widget
        this.setLayout( new BorderLayout() ) ;
        this.add( "West", new Label(
                "Enter your e-mail address"));
        this.add( "Center", _emailField) ;
        this.add( "South", buttonPanel ) ;
        // forward events to myself
        _doneButton.addActionListener( this ) ;
        _cancelButton.addActionListener( this )

Notice that in the widget subclasses Panel, I chose panel as the superclass so that I can inherit its layout capabilities. Also notice the class implements the ActionListener interface. This means that the class is allowed to listen to action events (the events that are generated by buttons). Toward the end of the constructor the class registers itself (in the call to addActionListener) as a listener of both push buttons.

2.2. Create the Event Classes

You must determine the events that your widget will generate. I have chosen to create a single event class, the EmailEntryEvent that can represent either “user is done” or “user canceled” state. When you create your event class keep in mind that it must maintain sufficient information to act on the event. In this example the event must store the e-mail address that was entered. Listing 2 shows the EmailEntryEvent class. Notice that I have created two constructors. When the constructor is passed an e-mail string, an e-mail entry event of type done is created. If no information is passed to the constructor, an event of type cancel is created. The e-mail entry widget has the responsibility of invoking the proper constructor (a reasonable request of the widget).

2.3. Create a Listener Interface

When an event is broadcast to a listener, specific methods of the listener class are invoked. You might think of these as “callbacks”. The listener interface defines these methods. The interface ensures that any class intended to be a listener has the methods needed to handle the event. The e-mail entry listener interface is shown in Listing 3. Any class that wishes to be a listener of EmailEntryEvents is required to implement a done method and a cancel method.