All Categories :
Java
Chapter 21
Creating User Interface Components
by David R. Chung
CONTENTS
The Java Abstract Window Toolkit (AWT) consists of classes that
encapsulate basic GUI controls. Because Java is a multiplatform
solution, the AWT provides a lowest common denominator interface.
Any interface you develop should appear about the same on any
platform. Often, the AWT is called Another Window Toolkit
or (affectionately) Awful Window Toolkit. Chapter 16, "The
Windowing (AWT) Package," provides an overview of the AWT.
Now don't be mislead-the AWT provides many useful controls and
your applications or applets may not require anything more. Sometimes,
however, an application needs something extra. This chapter examines
two methods for creating custom user interface components: subclassing
and combining controls.
Subclassing is just a fancy object-oriented term for changing
the way a class works. The actual method is to create a new class
from the old one and add new features along the way. In this chapter,
you learn how to extend the TextField class to create
a password control. The new class will be a TextField
that allows users to enter a password. Instead of displaying the
password, the control displays asterisks as the user types.
You create the passField class by subclassing the TextField
class. The control allows the user to type passwords. When the
user enters a character, you catch the keyDown event.
You keep track of the key the user pressed and place an asterisk
in the control (to keep the password hidden).
Notice that this control does not actually verify that the password
is valid. Instead, the control keeps track of user input and hides
the actual characters. Later in the chapter, you see how to combine
this control with others to create a useful password control.
The passField class has to keep track of the characters
entered by the user. To do so, we need a data member of type String:
class passField extends java.awt.TextField {
String pass ;
The class constructor has to know how many characters the control
can contain. To convey that information, the class constructor
takes an integer parameter, chars. Because passField
is derived from TextField, the first line in the constructor
must be a call to the superclass constructor.
NOTE |
To understand how classes are derived from (or extend) other classes, you must be familiar with some object-oriented terminology. In this example, passField is derived from TextField. TextField is referred to as the superclass, parent, or base class, while passField is referred to as the subclass, child, or derived class.
|
The call to super(int) actually calls TextField(int).
The next line in the constructor simply creates the String:
public passField( int chars ) {
super( chars ) ;
pass = new String() ;
}
The reason for subclassing this control is to hide user input.
To do this, the control must handle user input in the derived
class before the TextField class does. To handle user
input, this control overrides the Component class's keyDown()
method. This method is called every time a key is pressed.
The overridden keyDown() method must do the following:
- Add an asterisk to the control
- Store the actual key value entered
- Position the cursor at the end of the string
Because this class does not actually display the user-entered
values, the call to getText() returns a String
full of asterisks. Next, the method adds an asterisk to the String
and puts it back in the control. The select() method
is used to position the cursor at the end of the line; because
the two parameters are the same, nothing is actually selected.
public boolean keyDown( Event e, int key ) {
String text = getText() ;
setText( text + "*" ) ;
select( text.length() + 1, text.length() + 1 ) ;
The next item of business is to store the keystroke in a String.
Because the key parameter is an int, you must
cast it to a char and then use the String.valueOf()
method to convert it to a String. This new String
is then concatenated onto the existing String. Finally,
the keyDown() method has to return true because
it has fully handled the keystroke.
pass = pass + String.valueOf( (char)key ) ;
return true ;
}
The getString() method is provided to allow Containers
that use this control to get the user-entered value:
public String getString() {
return pass ;
}
}
To test the passField class, let's embed it in a simple
applet, as shown here:
import java.awt.*;
public class testPassField extends java.applet.Applet {
public void init() {
add( new passField( 40 ) ) ;
}
}
Figure 21.1 shows the testPassField applet.
Figure 21.1 : The testPassField applet.
If you run the applet and type some letters, you will see some
strange behavior. When you press Backspace, instead of
erasing an asterisk as you might expect, the control adds another
one! The reason is when the keyDown() method gets a keystroke-any
keystroke-it simply adds it to the string and displays another
asterisk.
To fix this behavior, you must somehow handle certain keystrokes
differently. One possibility is to add the following code to the
beginning of the keyDown() method:
if ( arg < 20 ) {
return false ;
{
This is only a partial solution, but it is a beginning. If the
keystroke has an ASCII value less than 20 (that is, if it is a
nonprinting character), then keyDown() should return
false. If keyDown() or any other event-handling
method returns false, the Event is passed on
to the superclass method. In this example, this small modification
causes the control to accept Backspaces. To really make this method
useful, you must also change the value of the String
to reflect any deletions.
If you have ever served on a committee, you know how hard it is
for a group of people to work together to reach a common goal.
Without leadership, everyone seems to go their own way. Without
well-coordinated communication, duplication of effort can occur.
Likewise, if you try to put together a Java applet by combining
several AWT controls, it may seem like you have a big committee-lots
of activity but no leadership and no communication.
However, if you combine your controls into composite controls,
they will then act like all the rest of the AWT controls. You
can use the new composite controls anywhere you use regular AWT
controls. To demonstrate composite controls, the following sections
explain how to create a scrolling picture window control. This
control takes an image and makes it scrollable. All the interaction
between the AWT components that make up the control is handled
internally. To use the control, all you have to do is create one
and add it to your applet's layout.
The base class for all composite controls is Panel. The
Panel class allows you to embed other AWT components.
This class is derived from Container, so it can contain
user interface (UI) components. The Panel class also
contains functions for managing embedded components.
Some functions in the Panel class can retrieve references
to the embedded components. These functions allow the class to
iteratively call methods in the embedded components. Other functions
handle layout issues.
PANELS ARE COMPONENTS, TOO
|
The primary advantage of using Panel as your composite component base class is that it is a Component itself. Consequently, you can use your composite components like any other AWT components. You can take these new components and combine them to form composite components from other composite components and so on.
The new components can be added to layouts; they can generate existing events or create new ones. They are full-fledged UI components and can be used anywhere that AWT components are used.
The composite controls will be more versatile if you implement them with the appropriate layout manager. Because you want the controls to be self-contained, they should be able to lay themselves out properly no matter what size they are.
|
In this example, you create a scrolling picture window. You derive
a class from Panel called ScrollingPictureWindow.
The class contains three member objects: an ImageCanvas
object (derived from Canvas) to hold the picture and
two scroll bars.
This composite control provides a self-contained way of displaying
a picture. A user simply has to pass an Image object
to the control and the control does the rest. The control handles
scrolling and updating the image. Figure 21.2 shows the scrolling
picture window applet.
Figure 21.2 : The testPictureWindow applet.
The testPictureWindow applet uses a ScrollingPictureWindow
object. The applet creates the ScrollingPictureWindow
in exactly the same way that you would use an AWT control.
The source code for the testPictureWindow applet is given
in Listing 21.1.
Listing 21.1. The source code for the testPictureWindow
applet.
import java.applet.*;
import java.awt.*;
import ScrollingPictureWindow ;
public class TestPictureWindow extends Applet {
ScrollingPictureWindow pictureWindow ;
public void init() {
Image img = getImage( getCodeBase(), "picture.gif" ) ;
pictureWindow = new ScrollingPictureWindow( img ) ;
setLayout( new BorderLayout() );
add( "Center", pictureWindow ) ;
}
};
The ImageCanvas class is derived from Canvas.
Canvas is provided in the AWT as a generic class for
painting and drawing. You use this class to display your Image.
The class contains one instance variable:
Image canvasImg ;
The ImageCanvas constructor takes an Image object
as a parameter. Because object parameters are passed by reference,
this makes a local reference to the Image object in the
class:
public ImageCanvas( Image img ) {
canvasImg = img ;
}
The only other method provided in the ImageCanvas class
is paint(). The paint() method actually draws
the image. Before doing any painting, however, the control has
to determine whether its parent is enabled. This check allows
the entire control to be turned off.
Because the picture scrolls, the class has to know where to draw
it. The location of the image depends on the position of the scroll
bars. In your scheme, the ScrollingPictureWindow object
handles communication between the member objects. You have to
query the ScrollingPictureWindow object to determine
where to draw the image:
public void paint(Graphics g) {
if ( getParent().isEnabled() ) {
g.drawImage( canvasImg,
-1 * ((ScrollingPictureWindow)getParent()).imgX,
-1 * ((ScrollingPictureWindow)getParent()).imgY,
this ) ;
}
}
To get the information, use the getParent() method. The
getParent() method is a member of the Component
class. This method returns a reference to the Container
object that holds the Component.
When you call getParent(), you get a reference to the
ScrollingPictureWindow object. Because this reference
is the Container type, you have to cast it to a ScrollingPictureWindow
reference. Now you can access the public instance variables in
the ScrollingPictureWindow object.
The imgX and imgY members contain the x and
y coordinates of the point (in terms of the Image) that
will be displayed in the upper-left corner of the window. If you
want the point (10,5) to be displayed in the upper-left corner,
pass -10 and -5 to drawImage().
The ScrollingPictureWindow class contains several instance
variables. These variables include the embedded controls and state
variables. The embedded controls are stored as follows:
public ImageCanvas imageCanvas ;
Scrollbar vertBar ;
Scrollbar horzBar ;
Image image ;
The last instance variable in this list is a reference to an Image
object, which is passed in by the owner of your class object.
The remaining instance variables all contain information about
the state of the control. The first two contain the size in pixels
of the entire image:
int imgWidth ;
int imgHeight ;
The next two instance variables contain the current position of
the image. These variables also reflect the current position of
the scroll bars. Because the scroll bars and the image are tied
together, both classes use these variables. The scroll bars set
their values, and the ImageCanvas uses these values to
place the image.
int imgX ;
int imgY ;
The last variable is used by the scroll bars. This value specifies
the amount the scroll bar moves when you request a page up or
page down.
int page ;
The class constructor performs all the initialization for your
class. The constructor must do the following:
- Initialize the state variables
- Determine the size of the image
- Instantiate the member controls
- Set up the GridBagLayout layout manager
- Set the constraints for each control
Initialize State Variables
Begin construction by setting the local Image reference
to the Image argument:
public ScrollingPictureWindow ( Image img ) {
image = img ;
The next step in the construction process is simple. You have
to initialize imgX and imgY to 0. What
this really does is set the initial position of the image and
scroll bars. These two instance variables contain the x and y
offsets at which to display the image:
imgX = 0 ;
imgY = 0 ;
The ImageCanvas class needs these variables to determine
how to place the image. The ImageCanvas paint()
method accesses these instance variables directly and uses them
in its call to drawImage().
Determine the Image Size
Your composite control has to know how large the image is. Once
you have this information, you know it will remain constant. Unfortunately,
determining the image size is not as straightforward as you may
think.
Your class has been designed to take an Image object
as a parameter, giving the users of the class a great deal of
flexibility in loading the image any way they want. The image
you receive may be one of many in an array. It may be in use by
other objects in the applet. It may also have been just recently
loaded by the calling applet. It is this last case that causes
problems.
In your class constructor, it is possible that the reference you
receive is to an image that is not yet fully loaded. To get the
image size, you make a call to Image.getHeight(). If
the image is not fully loaded, however, getHeight() returns
-1. To get the size of the image, you must loop until
getHeight() returns a value other than -1. Both
while loops that follow have null bodies:
while ((imgHeight = image.getHeight(this)) == -1 ) {
// loop until image loaded
}
while ((imgWidth = image.getWidth(this)) == -1 ) {
// loop until image loaded
}
Instantiate Member Controls
Next you must create the embedded member objects. The ImageCanvas
takes the Image as a parameter. The scroll bar constructors
each take a constant that determines whether the scroll bar is
vertical or horizontal:
imageCanvas = new ImageCanvas( image ) ;
vertBar = new Scrollbar( Scrollbar.VERTICAL ) ;
horzBar = new Scrollbar( Scrollbar.HORIZONTAL ) ;
Set Up GridBagLayout
You use a GridBagLayout to lay out the embedded control.
GridBagLayout is the most versatile layout manager in
the AWT, and it provides precisely the control you need to arrange
the components. GridBagLayout is the most powerful layout
manager.
First you create a GridBagLayout object. Then you call
setLayout() to make it the current layout manager:
GridBagLayout gridbag = new GridBagLayout();
setLayout( gridbag ) ;
Set Up Constraints for Each Control
The GridBagLayout class uses the GridBagConstraints
class to specify how the controls are laid out. First, you create
a GridBagConstraints object. Then you use the GridBagConstraints
object to determine how to lay out the individual components:
GridBagConstraints c = new GridBagConstraints();
You add the ImageCanvas object to your panel first. Because
the ScrollingPictureWindow control is supposed to act
like the native AWT controls, it must be resizeable. You have
to specify that the control can grow in both x and y directions,
so you set the fill member to BOTH:
c.fill = GridBagConstraints.BOTH ;
Because you want the image to fill all the available space with
no padding, set the weight parameters to 1.0:
c.weightx = 1.0;
c.weighty = 1.0;
Finish laying out the image by calling setConstraints()
to associate the ImageCanvas object with the GridBagConstraints
object. Then add the image to the layout:
gridbag.setConstraints(imageCanvas, c);
add( imageCanvas ) ;
Next you lay out the scroll bars. Start with the vertical scroll
bar. The vertical scroll bar should shrink or grow vertically
when the control is resized, so you set the fill member
to VERTICAL:
c.fill = GridBagConstraints.VERTICAL ;
Look at your layout in terms of rows. You see that the first row
contains two controls: the ImageCanvas and the vertical
scroll bar. You indicate that the scroll bar is the last control
in the row by setting the gridwidth member to REMAINDER.
c.gridwidth = GridBagConstraints.REMAINDER ;
Complete the vertical scroll bar layout by associating it with
the constraint object and then add it to the layout:
gridbag.setConstraints(vertBar, c);
add( vertBar ) ;
Finally, lay out the horizontal scroll bar. Because this scroll
bar should be horizontally resizeable, set its fill member
to HORIZONTAL:
c.fill = GridBagConstraints.HORIZONTAL ;
The reason for using a GridBagLayout layout manager is
to prevent the horizontal scroll bar from filling the entire width
of the control. You want to guarantee that the horizontal scroll
bar remains the same width as the ImageCanvas object.
Fortunately, the GridBagConstraint class provides a means
of tying the width of one object to the width of another.
You use the gridWidth member of the GridBagConstraint
class to specify the width of the scroll bar in terms of grid
cells. Set this member to 1 so that the horizontal
scroll bar takes up the same width as the ImageCanvas
object (they are both one cell wide). It is the ImageCanvas
object that sets the cell size.
c.gridwidth = 1 ;
The last thing you have to do is add the horizontal scroll bar.
First associate it with the constraints object and then add it
to the layout:
gridbag.setConstraints(horzBar, c);
add( horzBar ) ;
Depending on where your control is used, it may be resizeable.
You handle resizing by over-riding the Component.reshape()
method. This method is called every time a control is resized.
The first thing that your function does is call the superclass's
reshape() method. The superclass method does the real
work of sizing. Because you are using a GridBagLayout,
the LayoutManager resizes the individual components.
public synchronized void reshape(int x,
int y,
int width,
int height) {
super.reshape( x, y, width, height ) ;
You let the superclass do the resizing, so now you must update
the image and scroll bars. First, determine whether the width
of the control is greater than the image width plus the width
of the vertical scroll bar. If the control width is greater, disable
the horizontal scroll bar:
if ( width > imgWidth +
vertBar.preferredSize().width ) {
horzBar.disable() ;
If the control width is not greater, enable the horizontal scroll
bar:
} else {
horzBar.enable() ;
Next, determine how to reposition the horizontal scroll bar. Start
by getting the size of the entire control and the width of the
vertical scroll bar:
lllll Rectangle bndRect = bounds() ;
int barWidth = vertBar.preferredSize().width ;
NOTE |
When working with scroll bars, you have to set several values:
- The thumb position
- The maximum and minimum values
- The size of the viewable page
- The page increment
|
Now you can calculate the maximum value for the scroll bar. You
always set the minimum of the scroll bar to 0. The maximum
value is the image width minus the width of the ImageCanvas.
You set the page size and page increment to one-tenth of the maximum
size:
int max = imgWidth - (bndRect.width - barWidth);
page = max/10 ;
Before setting the new values, you must determine how to translate
the old position to the new scale. Start by getting the old maximum
value. If the old value is 0, you make the position 0:
int oldMax = horzBar.getMaximum() ;
if ( oldMax == 0) {
imgX = 0 ;
If the old maximum is not 0, you calculate the new position.
First, express the old position as a fraction of the old maximum.
Then multiply the fraction by the new maximum. The resulting value
gives you the new position:
} else {
imgX = (int)(((float)imgX/(float)oldMax) *
(float)max) ;
}
The last thing you have to do is set the scroll bar parameters:
horzBar.setValues( imgX, page, 0, max ) ;
horzBar.setPageIncrement( page ) ;
}
Use the same algorithm to set the vertical scroll bar.
The scrolling picture window control is especially concerned about
scroll bar events. All other types of events are passed on and
handled outside your program.
You start by overriding the Component.handleEvent() method.
In this method, you look for events generated by the horizontal
scroll bar. If the event is one of the seven scroll bar events,
you reset the imgX variable and call repaint().
You return true if you can handle the event.
public boolean handleEvent(Event e) {
if ( e.target == horzBar ) {
switch( e.id ) {
case Event.SCROLL_PAGE_UP:
case Event.SCROLL_LINE_UP:
case Event.SCROLL_ABSOLUTE:
case Event.SCROLL_LINE_DOWN:
case Event.SCROLL_PAGE_DOWN:
imgX = horzBar.getValue() ;
imageCanvas.repaint();
return true ;
}
The code for handling the vertical scroll bar is the same as for
the horizontal scroll bar. If you do not handle the event, call
the superclass handleEvent() method and return:
return super.handleEvent(e) ;
}
You now have a composite control that can become a drop-in replacement
for other AWT controls. It handles its own events and responds
to external resizing. The complete ScrollingPictureWindow
class appears in Listing 21.2.
Listing 21.2. The ScrollingPictureWindow
class.
import java.awt.*;
public class ScrollingPictureWindow extends Panel {
public ImageCanvas imageCanvas ;
Scrollbar vertBar ;
Scrollbar horzBar ;
Image image ;
int imgWidth ;
int imgHeight ;
int imgX ;
int imgY ;
int page ;
public ScrollingPictureWindow ( Image img ) {
image = img ;
imgX = 0 ;
imgY = 0 ;
while ((imgHeight = image.getHeight(this)) == -1 ) {
// loop until image loaded
}
while ((imgWidth = image.getWidth(this)) == -1 ) {
// loop until image loaded
}
imageCanvas = new ImageCanvas( image ) ;
vertBar = new Scrollbar( Scrollbar.VERTICAL ) ;
horzBar = new Scrollbar( Scrollbar.HORIZONTAL ) ;
GridBagLayout gridbag = new GridBagLayout();
setLayout( gridbag ) ;
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH ;
c.weightx = 1.0;
c.weighty = 1.0;
gridbag.setConstraints(imageCanvas, c);
add( imageCanvas ) ;
c.fill = GridBagConstraints.VERTICAL ;
c.gridwidth = GridBagConstraints.REMAINDER ;
gridbag.setConstraints(vertBar, c);
add( vertBar ) ;
c.fill = GridBagConstraints.HORIZONTAL ;
c.gridwidth = 1 ;
gridbag.setConstraints(horzBar, c);
add( horzBar ) ;
}
public synchronized void reshape(int x,
int y,
int width,
int height) {
super.reshape( x, y, width, height ) ;
if ( width > imgWidth + vertBar.bounds().width ) {
horzBar.disable() ;
} else {
horzBar.enable() ;
Rectangle bndRect = bounds() ;
int barWidth = vertBar.preferredSize().width ;
int max = imgWidth - (bndRect.width - barWidth);
page = max/10 ;
int oldMax = horzBar.getMaximum() ;
if ( oldMax == 0) {
imgX = 0 ;
} else {
imgX = (int)(((float)imgX/(float)oldMax) *
(float)max) ;
}
horzBar.setValues( imgX, page, 0, max ) ;
horzBar.setPageIncrement( page ) ;
}
if (height > imgHeight + horzBar.bounds().height) {
vertBar.disable() ;
} else {
vertBar.enable() ;
Rectangle bndRect = bounds() ;
int barHeight = horzBar.preferredSize().height ;
int max = imgHeight - (bndRect.height - barHeight) ;
page = max/10 ;
int oldMax = vertBar.getMaximum() ;
if ( oldMax == 0) {
imgY = 0 ;
} else {
imgY = (int)(((float)imgY/(float)oldMax) *
(float)max) ;
}
vertBar.setValues( imgY, page, 0, max ) ;
vertBar.setPageIncrement( page ) ;
}
}
public boolean handleEvent(Event e) {
if ( e.target == horzBar ) {
switch( e.id ) {
case Event.SCROLL_PAGE_UP:
case Event.SCROLL_LINE_UP:
case Event.SCROLL_ABSOLUTE:
case Event.SCROLL_LINE_DOWN:
case Event.SCROLL_PAGE_DOWN:
imgX = horzBar.getValue() ;
imageCanvas.repaint();
return true ;
}
} else if ( e.target == vertBar ) {
switch( e.id ) {
case Event.SCROLL_PAGE_UP:
case Event.SCROLL_LINE_UP:
case Event.SCROLL_ABSOLUTE:
case Event.SCROLL_LINE_DOWN:
case Event.SCROLL_PAGE_DOWN:
imgY = vertBar.getValue() ;
imageCanvas.repaint();
return true ;
}
}
return super.handleEvent(e) ;
}
};
class ImageCanvas extends Canvas {
Image canvasImg ;
public ImageCanvas( Image img ) {
canvasImg = img ;
}
public void paint(Graphics g) {
g.drawImage( canvasImg,
-1 * ((ScrollingPictureWindow)getParent()).imgX,
-1 * ((ScrollingPictureWindow)getParent()).imgY,
this ) ;
}
}
The versatility of composite, or extended, controls becomes apparent
when they are combined into a single applet. The applet presented
in this section is a password-protected picture control. This
control combines a passField control with a ScrollingPictureWindow
control. Figures 21.3 and 21.4 show the testPassWindow
applet.
Figure 21.3 : The testPassWindow applet before entering
the password.
Figure 21.4 : The testPassWindow applet after entering
the password.
The following applet code combines passField, ScrollingPictureWindow,
and Button objects. Because the passField and
ScrollingPictureWindow classes are self-contained, they
are used here just like the AWT Button control. Because
Java applets are reusable components themselves, the applet is
like a new control itself. In fact, in Java, you create applets
to embed in your Web pages the same way you create controls to
embed in your applets.
import java.applet.*;
import java.awt.*;
public class testPassWindow extends Applet {
passField passwordField ;
Button verifyButton ;
ScrollingPictureWindow pictureWindow ;
The init() method creates the member objects and then
places them in the control using a GridBagLayout (see
Chapter 16, "The Windowing (AWT) Package," for details
on GridBagLayout and the associated GridBagConstraints
classes). Notice that the passField and ScrollingPictureWindow
controls are used as if they were native AWT controls.
public void init() {
GridBagLayout gridbag = new GridBagLayout();
setLayout( gridbag ) ;
{
GridBagConstraints c = new GridBagConstraints();
passwordField = new passField( 30 ) ;
c.gridx = 1 ;
c.gridy = 1 ;
gridbag.setConstraints(passwordField, c);
add( passwordField ) ;
}
{
GridBagConstraints c = new GridBagConstraints();
verifyButton = new Button( "Verify" ) ;
c.gridx = 2 ;
c.gridy = 1 ;
gridbag.setConstraints(verifyButton, c);
add( verifyButton ) ;
}
{
GridBagConstraints c = new GridBagConstraints();
Image img = getImage( getCodeBase(), "picture.gif" ) ;
pictureWindow = new ScrollingPictureWindow( img ) ;
c.fill = GridBagConstraints.BOTH ;
c.gridx = 1 ;
c.gridy = 2 ;
c.gridwidth = 2 ;
c.weighty = 1 ;
gridbag.setConstraints(pictureWindow, c);
add( pictureWindow ) ;
}
pictureWindow.disable() ;
}
To tie all these controls together, it is necessary to handle
Button events. In this example, the Applet class
does the event handling. This combination of the password control
and the picture window control is a good candidate for placement
in a Panel so that it would become one big composite
control.
public boolean action( Event evt, Object arg ) {
if ( evt.target instanceof Button ) {
if ( ((String)arg).equals( "Verify" ) ){
if ( passwordField.getString().equals(
"secret" ) ) {
pictureWindow.enable() ;
pictureWindow.imageCanvas.invalidate() ;
pictureWindow.imageCanvas.repaint() ;
verifyButton.disable() ;
passwordField.disable() ;
}
return true ;
}
}
return false ;
}
};
Sometimes, your applets or applications require user interface
functionality beyond what is provided by the AWT. You can use
subclassing or composite controls to extend the AWT and create
new classes from the basic AWT classes.
When you extend the AWT, you can create self-contained controls
that respond to their own events. Your enhanced controls can often
be used as drop-in replacements for associated AWT controls.
The passField class developed in this chapter is an example
of a subclassed control. This class takes the basic functionality
of an AWT TextField and enhances it. The result is a
control that can be plugged in anywhere you might use a TextField.
The ScrollingPictureWindow class created in this chapter
is a good example of a composite control. This class combines
the techniques of subclassing and encapsulation. It also is a
subclass of Panel and serves to encapsulate the Canvas
control and two scroll bars.
When you design an applet or application in Java, you have at
your disposal the basic AWT controls. Now you can create enhanced
controls by combining and subclassing them. These new controls
will become part of your personal Java toolbox and you can use
them in all your future Java programming projects.
Contact
reference@developer.com with questions or comments.
Copyright 1998
EarthWeb Inc., All rights reserved.
PLEASE READ THE ACCEPTABLE USAGE STATEMENT.
Copyright 1998 Macmillan Computer Publishing. All rights reserved.