Wizard API updated!
Tim Boudreau has released a new version of the Swing Wizard library (version 0.997) that fixes the WizardException bug reported in JavaWorld's recent Open Source Java Project profile. The article's examples have been reworked to test out the new, improved WizardException. Thanks, Tim, for this helpful fix!
Open Source Java Projects: The Wizard API

Newsletter sign-up

Sign up for our technology specific newsletters.

Enterprise Java
View all newsletters

Email Address:

Put your user interface on a diet

Replace those heavyweight components with leaner, meaner lightweight components

A year or so ago, Java programmers building user interfaces (UI) had to live within the narrow confines of the Abstract Windowing Toolkit (AWT). Consequently, user interface designers had to contend with a paucity of user interface components, wide variations in their appearance and behavior across platforms, and a host of other undesirable features. It was an unpleasant time. Then, around the middle of last year, came the first release of Swing. Swing offered many more components, a common appearance, and identical behavior across platforms. It was a significant addition to the Java class library.

One of the key factors contributing to Swing's importance is that each user interface component in the Swing set is a lightweight component.

The user interface components supplied with early versions of Java -- which are still present in the java.awt package -- are called heavyweight components. They are called heavyweight components because a native user interface component is used to display each Java component -- a technique that gives Java applications the same look and feel as other applications written for a particular platform. These components are considered heavy because they require twice as many classes to implement -- a Java class plus its associated native class. They also have the unfortunate side effect of being opaque, which means that they can't be used to implement components with transparent regions, or components of non-rectangular shapes.

Lightweight components, on the other hand, have no native twin -- they are free to implement their own look and feel. Consequently, lightweight components were used as the basis for the Swing components in the JFC.

To better understand the technology behind the Swing set of user interface components and to help you see just how powerful lightweight components are, we'll build a lightweight component of our own. Because buttons are ubiquitous user interface components and are easy to understand in terms of their behavior, we'll implement a circular button. I'll assume you know how the Java UI model works as defined by the AWT and that you understand the new Java 1.1 event model.

Now, let's go to work!

Building a simple lightweight button component

We begin by creating a subclass of class java.awt.Component. Both heavyweight components and lightweight components share this common ancestor. By default, subclasses of class Component aren't associated with a native component, so they are "light" by default.

Because lightweight components lack an associated native class, they have no visual representation. Therefore, they must handle their own presentation, which they do by redefining their paint() method.

The following paint() method draws a circular button with a label. The label value (in _strLable) is set in the constructor.


public void paint(Graphics g)
{
Dimension dim = getSize();
int n = (dim.width < dim.height ? dim.width : dim.height) - 1;


// fill the interior of the button
g.setColor(getBackground());
g.fillArc(6, 6, n - 12, n - 12, 0, 360);


// draw a thin border around the button's interior
g.setColor(getBackground().darker().darker().darker().darker());
g.drawArc(6, 6, n - 12, n - 12, 0, 360);


// draw a thin ring around the entire button
g.drawArc(0, 0, n, n, 0, 360);


// draw the button's label
Font font = getFont();
if (font != null)
{
FontMetrics fontmetrics = getFontMetrics(font);
g.setColor(getForeground());
g.drawString(_strLabel,
n/2 - fontmetrics.stringWidth(_strLabel)/2,
n/2 + fontmetrics.getMaxDescent());
}


Here's what the circular buttons look like so far. Go ahead and try them out.



You need a Java-enabled browser to see this applet.



Note: This applet, and those that follow, require a fully JDK 1.1-compliant browser. Internet Explorer 4.0 qualifies, however, Netscape 4.0 does not -- at least not right out of the box. If you're using Netscape 4.0, surf on over to http://developer.netscape.com/software/jdk/download.html and install the upgrade.

Something's not quite right with the way the buttons work. Can you figure out what's missing?

Incorporating button feedback

Normally when you click on a button, you receive some sort of visual

feedback. For example, the selected button is generally highlighted to indicate it's selected.

To incorporate this type of feedback, we need to modify the paint() method. Let's assume that there is a variable named _boolPressed that reflects whether or not the button is being pressed. With that in mind, here's the new paint() method:


public void paint(Graphics g)
{
Dimension dim = getSize();
int n = (dim.width < dim.height ? dim.width : dim.height) - 1;


// fill the interior of the button **
if(_boolPressed)
{
g.setColor(getBackground().darker().darker());
}
else
{
g.setColor(getBackground());
}
g.fillArc(6, 6, n - 12, n - 12, 0, 360);


// draw a thin border around the button's interior
g.setColor(getBackground().darker().darker().darker().darker());
g.drawArc(6, 6, n - 12, n - 12, 0, 360);


// draw a thin ring around the entire button
g.drawArc(0, 0, n, n, 0, 360);


// draw the button's label
Font font = getFont();
if (font != null)
{
FontMetrics fontmetrics = getFontMetrics(font);
g.setColor(getForeground());
g.drawString(_strLabel,
n/2 - fontmetrics.stringWidth(_strLabel)/2,
n/2 + fontmetrics.getMaxDescent());
}


The sole change is marked with a double asterisk (**). The color of the interior of the button now depends on the value of the _boolPressed variable. This variable must in turn be set to true or false in response to user input with the mouse. To do that, we need to make our circular button listen to mouse events and mouse motion events.

First, we must enable the delivery of the specified events. We do this in the constructor:


public CircularButton(String strLabel)
{
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);


// store the label
_strLabel = strLabel;
}


The two invocations of the enableEvents() method tell the user interface framework that our component expects to receive both mouse events (for example, mouse button pressed, mouse button released) and mouse motion events (for example, mouse dragged) when they occur in regard to our button.

1 | 2 | 3 |  Next >
Resources
  • java.awt.Component http://www.javasoft.com/products/jdk/1.1/docs/api/java.awt.Component.html
  • Java AWTLightweight UI Framework http://www.javasoft.com/products/jdk/1.1/docs/guide/awt/designspec/lightweights.html
  • JavaSoft's tutorial for creating lightweight components http://java.sun.com/products/jdk/1.1/docs/guide/awt/demos/lightweight/index.html
  • Download this article and the complete source as a gzipped tar file /javaworld/jw-03-1998/howto/jw-03-howto.tar.gz
  • Download this article and the complete source as a zip file /javaworld/jw-03-1998/howto/jw-03-howto.zip
  • Previous How-To Java articles