Skip to main content

Using Swing's Pluggable Look and Feel

February 27, 2004

{cs.r.title}







Contents
What Does "Pluggable Look and Feel" Mean?
javax.swing.UIManager
The Java Look and Feel
Native Vs. Cross-Platform Look and Feel
Some Thoughts on Making Choices
Specifying a Default Look and Feel
Conclusion

This article discusses the Swing concept of a pluggable look and feel and offers some thoughts on how to use it in a way that is both convenient for the programmer and desirable for the user experience.

What Does "Pluggable Look and Feel" Mean?

Swing is a part of the Java Foundation Classes (JFC) and implements a set of graphical user interface (GUI) components with a pluggable look and feel (L&F). In very short terms, this means that Swing-based applications can appear as if they are native Windows, Mac OS X, GTK+, or Motif applications, or they can have a unique Java look and feel through the "Metal" package. Applications can also provide a completely new user experience by implementing a totally unprecedented L&F.

In its early days, Java offered only GUI elements that used native components (rendered and handled by the underlying windowing system). To be platform-independent, Java could provide only such elements and features that had counterparts on all supporting platforms. Today, we know that this was not enough for rich client applications. The growing demand for powerful UI elements led to the development of JFC and Swing, which overcame the limitations of the original implementation. Unlike their predecessors (the original AWT components are still available, of course), Swing components do not rely on the underlying windowing system; they are "lightweight," rendered entirely in Java. Having said that, one might argue that in a very strict sense they are not "native." For the Java look and feel, this is certainly true. And even the Windows look and feel in its current implementation only mimics its native counterpart. But this is not a general problem of Swing or the pluggable look and feel concept. We could write a new look and feel that uses native Windows APIs to create, paint, and maintain GUI elements.

"Pluggable" means that both visual representation and (physical) behavior of a GUI element can change, even while the component is on display, without modifying the program. When Swing applications create a new button by
instantiating the JButton class, this new object knows how it should paint itself on screen and how it should react to mouse movements and mouse clicks. However, it delegates these tasks (such as rendering and mouse handling) to certain specialized classes, because if the button itself contained the code that creates its visual representation, we would need to change this code or append new drawing methods if we wanted to modify its look. For this reason, Swing provides means to install and select custom look and feels as well as to create new ones. Implementing a new look and feel is a huge task, beyond the scope of this article; technical background information can be found in the excellent O'Reilly book Java Swing, by Marc Loy, Robert Eckstein, Dave Wood, James Elliott, and Brian Cole.

javax.swing.UIManager

Many L&F-related aspects are handled within the javax.swing.UIManager class. Applications use this class to query which look and feels are present, to obtain certain L&F names, and to set the look and feel the program wishes to use. At this point, we will have a short look at some of its interesting methods. We will cover them in greater detail later.

public static String getCrossPlatformLookAndFeelClassName()

This method returns the fully qualified class name of the so-called cross-platform L&F. If you want your application to have a unique Java look, choose the L&F that is returned by this method. We will have a closer look at this L&F in the next section.

public static String getSystemLookAndFeelClassName()

This method returns the class name of a L&F that implements the host environment's native look and feel. The name of the returned L&F, therefore, is platform-dependent. If you want your application to appear as native applications do, you might choose the L&F returned here.

public static UIManager.LookAndFeelInfo[] getInstalledLookAndFeels()

This method returns a list of currently available L&Fs. In addition to the cross-platform and native look and feels, the Java runtime usually provides the Motif L&F. As I have mentioned earlier, Swing provides the means to create a completely new look and feel. If such a package is installed properly, it will be returned here, as well.

public static void setLookAndFeel(String className)

Finally, this method is used to set the current default look and feel.

The Java Look and Feel

Now we will have a closer look on how pluggable look and feel affects programming. Consider the following minimal Swing program.

import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;

public class SuperSimple2 extends JFrame {
 
  JLabel msgLabel;
 
  public SuperSimple2() {
    super("SuperSimple");
    setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });

    ActionListener al = new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        msgLabel.setText(((JButton)e.getSource()).getText());
      }
    };
    JButton button;
    JPanel buttonPanel = new JPanel();
    buttonPanel.setBorder(
      new TitledBorder("please click a button"));
    for (int i = 0; i < 3; i++) {
      button = new JButton("Button #" + (i + 1));
      button.addActionListener(al);
      buttonPanel.add(button);
    }

    JPanel cp = new JPanel(new BorderLayout());
    cp.setBorder(new EmptyBorder(10, 10, 10, 10));
    msgLabel = new JLabel("no button clicked!");
    cp.add(msgLabel, BorderLayout.NORTH);
    cp.add(buttonPanel, BorderLayout.CENTER);

    setContentPane(cp);
    pack();
    setVisible(true);
  }
   
  public static void main(String [] args) {
    new SuperSimple2();
  }
}

The program does the same thing, displaying a window containing some buttons, no matter which operating system your machine has. Its visual appearance will differ, though. On a Mac, it will (at least to a certain extent) look like a
native application. Other systems probably show something similar to Figure 1:

Figure 1
Figure 1. The Java look and feel

What we see here is called the Java look and feel. Since the package that contains this L&F is named javax.swing.plaf.metal, it is commonly referred to as Metal. Metal has been Sun's visual appearance of choice for Java programs. With J2SE 1.5, we will get a slightly new look, called Ocean, that will replace the old one (now to be called Steel). Technically, Ocean is not a new L&F but implemented as javax.swing.plaf.metal.MetalTheme, and therefore aims to be compatible with existing Metal applications (concerning getPreferredSize(), for example). The Java look and feel is often referred to as a cross-platform look and feel, because it should be available on any environment providing Java and JFC.

Native Vs. Cross-Platform Look and Feel

If you look at Figure 1, you might argue that the program does not look familiar. The term "look and feel" refers to the visual appearance and physical behavior of GUI components; however, it covers additional aspects that go beyond that component level. It is also about terminology, layout, and general program behavior. For example, on some platforms you "quit" an application, on others you "exit;" some have one main application window with several internal frames, while others prefer multiple top-level windows. Or think of the way a program is launched or where it puts its files and settings. We might call this user experience. A program may use GUI elements that look like (or even are) native ones, yet it still may feel strange if it behaves different than the majority of native applications do. Why is that? Through our daily routine, we get used to certain fonts and colors; we expect things to be at a certain place. A Windows XP user expects to close a window by clicking on a red widget in the upper right corner of the window. Consequently, a program with a closing element in the upper left corner of its window is likely to feel strange to him/her because he/she is not used to that. The same is true for Mac users: a program with a menu bar inside of the top level window surely feels unfamiliar, since Mac applications usually have one menu bar at the top of the screen.

So why does our program use Metal? UIManager maintains a default look and feel that is chosen if an application does not set one explicitly. On many systems, this will be Metal.

The underlying question is: should a Java program look and feel native anyway? One of the great ideas behind Java has always been "write once, run anywhere." Taking that into account, shouldn't a Java program look and feel the same, regardless which platform it runs on? Wouldn't it be desirable if all Java programs had the same appearance? A program can provide a unique user experience through the Java look and feel and through a very comprehensive set of style guides, which you can find in the book Java Look and Feel Design Guidelines. It provides ample information about how a program using the Java look and feel should behave; how it should organize its menus and dialogs; and which colors, fonts and icons to use. Sun once coined the term "100% pure Java." If you want your program to be recognized as a Java application, you should use the Java look and feel and provide a user experience according to the guidelines of this book. Sun surely promotes this idea. And that is why on many systems, Metal is the default look and feel. On the other hand, many people consider Metal ugly. I definitely won't comment on that since this is personal taste. However, several points back Metal's bad reputation:

  • There are several alternative look and feels available.
  • There are even completely new windowing toolkits. One that gained widespread public recognition is
    SWT, the Standard Widget Toolkit which is used in the Eclipse universal tool platform.
  • J2SE 1.5 brings a sort of brushed-up, new look.

Time will tell if Ocean achieves a better reputation than Steel did. Figure 2 shows the new look.

Figure 2
Figure 2. SwingSet using the new J2SE 1.5 Ocean look

As I have already pointed out, users get used to certain behavior (remember my window-closing example) and often work with several programs simultaneously. If the appearance and behavior of our Java program differs significantly from native applications, users are likely to get confused and frustrated. Additionally, many people have strong expectations concerning visual appearance. That is why on a Mac, my sample program won't show the Java look and feel — the Mac defaults to its own system look and feel. Java programs need to integrate smoothly into the Mac OS X environment, otherwise Mac users will never accept such a program. For these reasons, many Swing programs contain code similar to this:

try {
  UIManager.setLookAndFeel(
    UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
}

Forcing the program to use the system look and feel undoubtedly has several advantages:

  • The look and feel is familiar to the user.
  • It is not immediately visible that the program is written in Java.

Unfortunately, this has a seldom-acknowledged drawback. Although getSystemLookAndFeelClassName() returns a L&F that claims to provide a native look, the user might have installed one that even better resembles the host environment. But in current versions of Java, the value returned by this method is hard-coded. Sun must have been aware of this issue, since the upcoming J2SE 1.5 allows the user to set the return value of getSystemLookAndFeelClassName().

With J2SE 1.4.2, Sun introduced the GTK+ look and feel. On many Linux-based systems it would be adequate to return it as the native L&F. But currently, this is not the case. Additionally, getInstalledLookAndFeels() does not mention this L&F at all. Sun has fixed this in J2SE 1.5; however, it may still take some time until this version will gain widespread use. As a result, on Linux, most Java programs show the Motif look and feel.

On Windows systems, there are several issues with Sun's implementation of the Windows look and feel. If an application uses it, its components may not look or behave exactly like native ones. This is because the Windows L&F only mimics its native counterparts, rather than using them. The WinLAF project on java.net aims to fix these glitches. If you are interested in what these problems are, please have a look at WinLAF's release notes. Of course, it would be best if the underlying issues were fixed in Java itself. Until this is the case, the WinLAF project provides a way to make Java applications with the Windows look and feel appear as close to their native counterparts as possible. Figures 3 and 4 illustrate the sometimes subtle differences.

Figure 3
Figure 3. SwingSet using Sun's Windows look and feel

Figure 4
Figure 4. SwingSet using WinLAF's look and feel

How can applications benefit from the WinLAF project? According to the installation instructions, using WinLAF requires two steps:

  1. Put the winlaf-0.4.jar file in the CLASSPATH of your application.
  2. In your main() method, include a line of code like this: net.java.plaf.LookAndFeelPatchManager.initialize();

However, this ties an application to the WinLAF project, which is not what we want. But it takes just a few steps to make Swing applications benefit from the WinLAF corrections. These are:

  1. Copy the WinLAF .jar file to the ext directory of your Java installation.
  2. Create a little wrapper class and put it in a .jar file.
  3. Put that jar file into the ext directory as well.
  4. Modify the swing.properties file to include the new look and feel and set the L&F as the default one.

The code of the little wrapper class is as follows.

package com.sun.java.swing.plaf.windows;

public class WinLAFWrapper extends WindowsLookAndFeel {
   
  public WinLAFWrapper() {
    try {
      javax.swing.UIManager.setLookAndFeel(this);
      net.java.plaf.LookAndFeelPatchManager.initialize();
    } catch (Exception e) {
    }
  }
}

Some Thoughts on Making Choices

Let us reconsider the alternatives concerning choosing a look and feel:

  1. Choose the cross-platform look and feel.
  2. Choose the native L&F.
  3. Provide an individual look and feel and force Java to use this.
  4. Let the user decide.

Any of the first three has the disadvantage that it does not respect users' preferences. Programs that force Java to use a specific look and feel to render its components will not benefit from the WinLAF project; neither will they show the appropriate system look and feel on some operating systems. As we have seen, there might be an enhanced version of the system look and feel, and it is quite probable that the users would like to use this. And on some systems, Java simply assumes the wrong look and feel to be the native one. Consequently, the program must allow the user to decide which look and feel to use. The main reason is flexibility. If someone likes the Java look, he/she can choose it. If others prefer a tight integration into the host environment, let them have it. And the ones in favor of really individual look and feel may choose their preferred one, too. Technically, this is not a big deal. You can query the available look and feels using getInstalledLookAndFeels() and set one with another method of UIManager; for example, public static void setLookAndFeel(LookAndFeel newLookAndFeel) or public static void setLookAndFeel(String className).

However, since there is no standard dialog box for selecting L&Fs, applications usually implement one on their own. Although it is desirable that users can choose which look and feel the application should use, doing so should be the same in all applications. Clearly, the best solution would be if Sun provided a standard look and feel chooser. Since this probably will not be the case for some time, it is perfectly valid to provide one. But please consider the following question: why do users have to select a preferred look and feel for almost any application they install? Swing provides means to set a look and feel as the default one. If a user has done so, it is quite likely that this is the one he/she likes best. Why can't an application respect his/her decision?

Specifying a Default Look and Feel

Several L&F-related settings can be managed through the swing.properties file, which resides in %JAVA_HOME%\lib or $JAVA_HOME/lib, depending on your operating system. This file may not be present. If it is not, you can create it using an editor that can save plain ASCII files. A more pleasant way is to use a specialized tool such as TKPLAFUtility.

Figure 5
Figure 5. The TKPLAFUtility program

The default look and feel is specified through the swing.defaultlaf property. You can set it in the swing.properties file or set it as a command-line parameter. The value of this property is the fully qualified class name of the look and feel to use. In an application, you can use the method public static LookAndFeel getLookAndFeel() to determine it.

Conclusion

This article introduced and discussed some aspects of the pluggable look and feel concept of the Swing GUI elements. My goal has been to make you aware of some issues that might arise while utilizing the concept.

Thomas Kunneth works as a software architect at the German authorities, specializing in Java-based rich clients.
Related Topics >> GUI   |   Swing   |   

Comments

You can find one of exciting Look and Feels ...

You can find one of exciting Look and Feels here:
http://weblookandfeel.com

WebLookAndeel is a large LaF/components library that provides gorgeous Swing skin
and additional Swing components for different cases.

It is fully cross-platform and based on two licenses - GPLv3 and commercial one.
So You can use it for free in any open-surce project if You want :)