Software Development optimize

Using pluggable look-and-feel in Java Swing APIs


The Java Swing API provides a pluggable look-and-feel (PLAF) capability, which allows Swing GUI widgets to change appearance based on the programmer's customized look-and-feel setting. Almost all modern user interface frameworks coalesce the view and controller, whether they are based on SmallTalk, C++, or Java.

Swing packages each component's view and controller into an object called a UI delegate. For this reason, Swing's underlying architecture is referred to as model-delegate rather than a model-view-controller. Ideally, communication between both the model and the UI delegate is indirect, allowing more than one model to be associated with one UI delegate and vice versa.

UI delegates

Each UI delegate is derived from an abstract class called ComponentUI. ComponentUI methods describe the fundamentals of how a UI delegate and a component using it will communicate. Note that each method takes a JComponent as a parameter. ComponentUI has many methods, but here are the most important ones:

  • static ComponentUI createUI(JComponent c): This method is normally implemented to return a shared instance of the UI delegate defined by the defining ComponentUI subclass itself. This instance is used for sharing among components of the same type (e.g., all JButtons using the Metal look-and-feel share the same static UI delegate instance defined in javax.swing.plaf.metal.MetalButtonUI by default).
  • installUI(JComponent c): This method installs this ComponentUI on the specified component. This normally adds listeners to the component and/or its model(s) to notify the UI delegate when changes in state occur that require a view update.
  • update(Graphics g, JComponent c): If the component is opaque, this should paint its background and then call paint(Graphics g, JComponent c).
  • paint(Graphics g, JComponent c): This method gets all of the information it needs from the component and possibly its model(s) to render it correctly.

To enforce the use of a specific UI delegate, you can call a component's setUI() method like this:

JButton m_button = new JButton();

m_button.setUI((MyButtonUI)MyButtonUI.createUI(m_button));

The JComponent class defines methods for assigning UI delegates because the method declarations required do not involve component-specific code. However, this is not possible with data models because there is no base interface that all models can be traced back to (i.e., there is no base class such as ComponentUI for Swing models). For this reason, the methods to assign models are defined in subclasses of JComponent where necessary.

Put PLAF to use

Swing includes several sets of UI delegates. Each set contains ComponentUI implementations for most Swing components, and each of these sets is called a PLAF implementation.

The javax.swing.plaf package consists of abstract classes derived from ComponentUI, and the classes in the javax.swing.plaf.basic package extend these abstract classes to implement the Basic look-and-feel. This is the set of UI delegates that all other look-and-feel classes are expected to use as a base for building off of. The Basic look-and-feel cannot be used on its own because BasicLookAndFeel is an abstract class. There are three pluggable look-and-feel implementations derived from the Basic look-and-feel:

  • Windows: com.sun.java.swing.plaf.windows.WindowsLookAndFeel
  • CDE\Motif: com.sun.java.swing.plaf.motif.MotifLookAndFeel
  • Metal (default): javax.swing.plaf.metal.MetalLookAndFeel

There is also a MacLookAndFeel for simulating Macintosh user interfaces, but this does not ship with Java 2 and must be downloaded separately. The multiplexing look-and-feel, javax.swing.plaf.multi.MultiLookAndFeel, extends all of the abstract classes in javax.swing.plaf. It is designed to allow combinations of look-and-feels to be used simultaneously and is intended for, but not limited to, use with Accessibility look-and-feels. The job of each multiplexing UI delegate is to manage each of its child UI delegates.

Each look-and-feel package contains a class derived from the abstract class javax.swing.LookAndFeel: BasicLookAndFeel, MetalLookAndFeel, WindowsLookAndFeel, etc. These are the central points of access to each look-and-feel package. You use them when changing the current look-and-feel, and the UIManager class (which is used to manage installed look-and-feels) uses them to access the current look-and-feel's UIDefaults table (which contains, among other things, UI delegate class names for each Swing component's corresponding look-and-feel).

To change the current look-and-feel of an application, you can simply call the UIManager's setLookAndFeel() method, passing it the fully qualified name of the LookAndFeel to use. You can use the code in Listing A to accomplish this at runtime.

SwingUtilities.updateComponentTreeUI() informs all children of the specified component that the look-and-feel has changed and that they need to discard their UI delegate in exchange for a different one of the specified type.

Figures A, B, and C illustrate the difference between Metal, Motif, and Windows look-and-feel.

Create a PLAF

Before creating a PLAF, you must decide between two design methods for creating a look-and-feel in Java. One method is to create the look-and-feel by extending the javax.swing.plaf package; the other is to extend an existing look-and-feel package, usually javax.swing.plaf.basic.

It not recommended to extend a look-and-feel from the javax.swing.plaf package if the look-and-feel is going to be for a PC. This is because the javax.swing.basic package has extended almost the entirety of the javax.swing.plaf package for you to use. This allows you to pick and choose which things about the look-and-feel to customize without having to extend and implement everything.

In its implementation of the javax.swing.plaf package, a basic principle is followed that allows for customizing a look-and-feel very easily. This principle is the centralization of components, color, and UI classes within the LookAndFeel class.

The javax.swing.plaf.basic package paints the lightweight Swing components in expected ways. However, the recommendation changes if you're creating a look-and-feel for a device other than the computer screen. Then, the preferred method is extending the javax.swing.plaf package from scratch.

Peter V. Mikhalenko is a Sun certified professional who works for Deutsche Bank as a business consultant.

---------------------------------------------------------------------------------------

Get Java tips in your inbox Delivered each Thursday, our free Java newsletter provides insight and hands-on tips you need to unlock the full potential of this programming language. Automatically subscribe today!
4 comments
zdw_1217
zdw_1217

I think creating the UI class for new L&F is a little out of date... The JRE5+ provide synth look and feel, which allow writting xml to define a new look and feel. www.easynth.com even provide an IDE for designing synth based L&F.

sumittoskar
sumittoskar

Do you have sample coding of Mac Look N Feel on windows machine using java? A simple example of JFrame. Changing the frame colour, change frame close, minimize & maximize button image or colour. Please send me on my email id as mention below. sumittoskar@yahoo.co.in

mykros
mykros

This article only describes the easy stuff, that has been written hundreds of times. A more interesting problem is how can you use other look-and-feels (LAF) 'on the fly' without having the software knowing the particular look and feel in advance. First it may be wise to define a special directory where you can drop the *.jar files providing different LAF's. Then you have to implement your own classloader to load the files from that directory. I have to say it was extremely annoying to implement this. It's also wise to add a fallback to the default LAF shipped with the JRE if the last selected LAF is no longer available (deleted *.jar file or whatever happend) but stored in the configuration file of your application. There are also LAF's available supporting different styles (other color sets). I don't know if there is a automated/build-in way to get this information and to display a style selection dialog where the user can select one of the styles. Creating an own dialog for this doesn't seem to make sense as you do not know what the LAF supports.