HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

The idea of the AWT (the Abstract Window Toolkit) is to make a platform-independent way to build UIs. That is a noble and worthwhile goal, to be sure. Unfortunately the AWT falls into the same traps that so many of the other tools targeting platform independence did years ago. Walking the tightrope between usefulness and platform independence, the AWT often fails to achieve one for the sake of the other.

A prime example is AWT heavyweight components, especially with respect to text rendering. All windowing platforms have a similar concept of text rendering and fonts. If you've developed in MS Windows, any of the Mac OS's, Unix X-Window or any other platform targeted by the AWT, you'll recognize a similar functionality presented by the AWT's Font class (and its relationship to the Graphics class).

Let's start by concentrating on the Java 1.0 AWT (later we'll see how Java 1.1 is a little better at doing text rendering and Component presentation). In 1.0, every UI Component object has its own default Font that is to be used when rendering text on that Component. The Component class, which is a base class for all UI Component classes in the AWT (both functional heavyweights such as Choice, List and Button as well as customizable heavyweights such as Canvas, Panel and Frame), has two methods to support this: getFont() and setFont(). In addition, each Component object has a package-private member named (appropriately enough) "font". The Component class implementation of getFont() looks something like this:

public Font getFont() {
if(null != font)
return font;
if(null != parent)
return parent.getFont();
return null;
}

This means that if a UI component doesn't have a display font that has been set explicitly (using the Component's setFont() method), then the component uses its parent container's default font for rendering text automatically. This is nice because it means if you want to set a default font to be used by all components within a UI, just set the default font of the top-most Frame window in your UI. All other components that are children of that Frame will use the parent Frame's default font.

And for those of you who don't know how it's done, this is how a component's default font actually gets used. In order to render itself to the screen, each Component object's paint() method is called at appropriate times. The paint() method is handed, as an argument, a Graphics object. By the time paint() is called, the Graphics object's text rendering font has been set to the component's default font.

How This Is Done
It turns out that a component object's paint() method is almost never called directly. Instead, each component's update() method is called. The update() method is declared in the Component class definition, and given an implementation like this:

public void update(Graphics g) {
g.setColor(getBackground());
g.fillRect(0, 0, width, height);

g.setColor(getForeground());
g.setFont(getFont());

paint(g);
}

As long as the call to a Component's paint() method is invoked by that object's update() method, then the component's default Font is automatically selected into and used by the Graphics object. That's the idea, anyway.

This is why if you implement a component to just paint a piece of text (by extending the Canvas, Panel, Dialog or Frame classes with your own class and implementing the paint() method), it's the component's default font that will be used to draw the text.

Problems start when you try to use the so-called heavyweight component classes included with the AWT. These include the Button, List, Checkbox, Choice and Label Component classes. The design of these classes, and the definition of the 1.0 AWT, is such that use of default fonts in these types of components actually may not work. For example, if I wanted to make a Button in my UI and have that Button display its command label using a 30 point, bold Helvetica font, most 1.0 AWTs wouldn't use my font of choice. Instead of using the font I gave to the Button object's setFont() method, the Button would just display on the screen using an AWT-wide default display font. The font I specified to the Button's setFont() method would be completely ignored.

I don't know for sure why most AWT heavyweight components act this way, but it seems to be a pervasive problem for most 1.0 AWT implementations. I've witnessed the problem using many different 1.0 MS Windows and Mac VMs. It seems that a Button, or a List, or a Choice object really was meant only to be displayed exactly one way in these AWT implementations. That one display presentation uses the exact same display font, background color and foreground color for all heavyweight components in the AWT. Note that these are reportedly some VMs that have better AWT implementations. That is, VMs that in fact use the component's own default font, background color and foreground color for display of heavyweight components. I think, however, that these VMs are by far in the minority.

I've noticed that the JDK's 1.1 AWT implementation no longer has this "system-wide display font" problem with heavyweights. Instead, you can set the display fonts, background colors and foreground colors individually for all components and they actually are reflected in the display. (Note that this article was completed before I had a chance to experiment with the MS Internet Explorer 4.0 VM, which is a near-1.1 VM and new AWT implementation, so that too may no longer have this problem).

After probably taking too much time explaining the problem I wanted to talk about, I'm now going to describe the solution: only use heavyweight components in the rarest of circumstances. Due to the fact that the List, Label, Choice, Button and Checkbox classes were designed to give you a lowest-common-denominator functionality across all windowed operating systems, they end up being all but useless. Only in rare situations do you really have need for such simplistic, visually ugly UI components.

There's another problem with these types of components: they actually end up looking radically different when displayed with different AWTs. A Button object displayed using an MS Window's AWT is going to look very different than one using an X-Window's AWT. The one in Windows will look like a Windows button, and the one in X-Windows will look like an X button widget.

Some developers I've talked to see system-dependent display as a good thing. They like the fact that components will display more like what the user expects and is used to. The user will know on a Mac machine what a Mac button does. I agree that this may be a nice feature of heavyweight components such as Buttons. However, the Mac windowing system also has several UI component types with absolutely no equivalent in the AWT; Hierarchical tree components, tool palettes, etc. There are no AWT equivalents for these MAC UI components. So, to achieve similar functionality within a Java UI, you must create (or purchase from a third party) custom components. And these components, though achieving the same functionality as the Mac UI components, will almost certainly not look the same as the Mac equivalents.

Listing 1 is code for a ClickableLabel custom component, written entirely within Java. A ClickableLabel has the same capabilities as a heavyweight Label component, with the added ability to receive and handle mouse clicks. Note that I had to re-implement Label functionality within my Canvas-derived class. This is an unfortunate fact about heavyweights, you just can't count on being able to derive new classes from them to add your own functionality. Most AWT implementations do not behave as expected when you derive > from a heavyweight class.

So, for a UI with all but the most trivial, dialog-like interface, you're going to have to show the user UI components that are maybe not exactly what the user has seen before. That is, using custom components that are not necessarily targeted towards the underlying target platform. This is a given. There's almost no way around this problem. So, why not just build the entire UI out of custom components?

Evidence supporting the strategy of building entirely pure Java custom components based on the Canvas, Panel, Dialog or Frame classes exists in two new component packages: Sun's JFC (Java Foundation Classes) and Microsoft's AFC (Application Foundation Classes). These are two new sets of entirely pure- Java UI elements. The idea is that, when building UIs in Java, use of pure-Java components will make your interfaces guaranteed not to be platform-dependent. They will look the same, no matter what underlying windowing system is used to display them.

That last statement is not exactly true. Sun's JFC UI components have a set of requirements which include having a pluggable "look and feel". This means you can tell a given JFC UI component to act Mac-ish, or MS Windows-ish or to target a look and feel like any particular underlying windowing platform. With the JFC classes you end up having the option of visually presenting the user with a look and feel he is comfortable with. Unfortunately, the set of "looks and feels" could never encompass all potential target platforms, for each individual UI component. Maybe that's a goal of the JFCs, but I just don't see it happening because of all the work that it would entail. For popular platforms (Mac, MS Windows, X-Motif), you'll have looks and feels available. For most other platforms (WebTV-type boxes, gaming systems, etc.) you're going to have to "roll your own" pluggable "look and feel", most likely.

Note that Sun's JFC components are designed to work with 1.1 VMs and AWT implementations. This is the one drawback I see to using the JFC classes. The AFC classes will work on any 1.0 Java platform. On the other hand, the JFC classes are destined to become part of the standard core Java platform, meaning they will be available eventually in every browser and standalone VM. The AFCs, being Microsoft's preferred UI toolkit, may become available on all Microsoft VMs, but the chances are slim that they will become part of a standard Java platform.

In addition to the JFC and AFC class packages, several different third-party vendors have built UI component packages you can purchase and distribute. Again, these third-party packages are designed around the pure-Java component idea. No matter which underlying platform these third-party UI components are deployed on, they will always work the same and display the same. The difference between third-party packages and the JFC classes or AFC classes is that third-party packages will have to be purchased. Both the JFC and AFC packages are free. On the other hand, many third-party packages have some pretty nice visual components such as pie charts, bar graphs, etc. Buying such component packages generally will end up being a lot cheaper than rolling your own.

In any event, I've tried to lay out an argument for basically ignoring the heavyweight classes distributed with the 1.0 Java AWT. These classes are the List, Button, Choice, Checkbox and Scrollbar classes. Instead, to ensure platform-independence, you should stick to using truly pure-Java UI components. Those components may be Canvas, Panel, Frame or Dialog-derived classes. You can build your own (an example is given in Listing 1), use Sun's JFC or Microsoft's AFC UI component packages, or purchase third-party packages. I can guarantee you'll be happier with the results.

About the Author
Brian Maso is a programming consultant working out of Portland, OR. He is the co-author of The Waite Group Press's upcoming release, "The Java API SuperBible." Before Java, he spent five years corralled in the MS Windows branch of programming, working for such notables as the Hearst Corp., first DataBank, and Intel. Readers are encouraged to contact Brian via e-mail with any comments or questions at bmaso@europa.com

	

Listing 1. 
 
A simple custom component named ClickableLabel. All custom UI components in 1.0 
are derived from Canvas, Panel, Frame or Dialog (1.1 allows you to derived from 
Component, Container and  Window also). 

public class ClickableLabel extends Canvas { 
  protected String m_strLabel; 
  protected int m_hpad = 2; 
  protected boolean m_fMouseDown; 
  protected boolean m_fMouseIn; 

  public ClickableLabel(String strLabel) { 
    setLabel(strLabel); 
  } 

  public void setLabel(String strLabel) { 
    m_strLabel = new String(strLabel); 
  } 

  public String getLabel() { 
    return new String(m_strLabel); 
  } 

  public void paint(Graphics g) { 
    // ensure foreground color and preferred 
    // font are selected into the Graphics 
    g.setColor(getForeground()); 
    g.setFont(getFont()); 

    FontMetrics fm = g.getFontMetrics(getFont()); 
    int h = fm.getHeight(); 
    int ch = size().height; 
    int texttop = ch/2  h/2; 
    int y = texttop + fm.getAscent(); 
    g.drawString(m_strLabel, m_hpad, y); 
  } 

  public Dimension preferredSize() { 
    Graphics g = getGraphics(); 
    FontMetrics fm = g.getFontMetrics(getFont()); 
    g.dispose(); 

    return new Dimension(fm.stringWidth(m_strLabel) + 
        2 * m_hpad, fm.getHeight()); 
  } 

  public Dimension minimumSize() { 
    return preferredSize(); 
  } 

  // A mouse click event causes an ACTION_EVENT to be fired 
  // by this ClickableLabel 
  public boolean mouseEnter(Event evt) { 
    m_fMouseIn = true; 
  } 

  public boolean mouseExit(Event evt) { 
    m_fMouseIn = false; 
  } 

  public boolean mouseUp(Event evt) { 
    if(m_fMouseIn && m_fMouseDown) { 
      Event evtAction = new Event(this, 
          Event.ACTION_EVENT, 
          getLabel()); 
      postEvent(evtAction); 
    } 
    m_fMouseDown = false; 
  }


 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: info@sys-con.com

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.