Introduction
Besides enhanced looks and advanced features, one of the best things about Swing is its pluggable look and feel (PLAF). PLAF architecture allows seamless changes in the appearance of an application and the way an application interacts with the user. However, designing and developing a PLAF is much more exhaustive and complex. On the other hand, themes provide a simple alternative to change look and feel of the swing application. Themes are easier to implement and they enhance the visual appeal of the application UI using the default Java look and feel.
Theme mechanism allows a developer to easily specify the default colors, fonts and icons used by the look and feel (L&F). It allows developers to write their own themes; giving them a choice to show their application GUI the way they want it, rather than depending on the defaults provided by the system.
This article discusses how to use different themes for Swing's default "Metal" look and feel. The metal look and feel, which is also known as Java look and feel is supported on all Java 2 platforms.
Metal Themes
Default theme of Swing's metal look and feel is "Steel". The class behind Steel theme is - javax.swing.plaf.metal.DefaultMetalTheme
, which extends the abstract
base class MetalTheme
. The class DefaultMetalTheme
overrides all the required methods, which give the Java look and feel (aka Metal L&F) its default "Steel" theme. This class implements various methods, which are of the form getXXX()
such as getMenuTextFont()
, getPrimary1()
, getSecondary1()
etc. These methods return the default primary colors, secondary colors and various fonts used by the theme for the GUI.
Any application, which intends to use its own theme for the UI can subclass the DefaultMetalTheme
class, overriding only the required methods.
This application provides two approaches for using these themes. One is in-built themes, and the other one is custom themes, which allows the theme to be read from a property file written in a specific format. The custom theme approach allows more flexibility and convenience of deploying various themes without changing the compiled code. Both the approaches use a class, which is a subclass of DefaultMetalTheme
.
Built-in Themes
Besides Java's default Steel theme, this application uses two more built-in themes - White Satin and Moody Blues. These built-in themes are implemented as classes. Each one of them extends the class DefaultMetalTheme
. The code for the class MoodyBlueTheme
is given below:
import javax.swing.plaf.*;
import javax.swing.plaf.metal.*;
public class MoodyBlueTheme extends DefaultMetalTheme
{
public String getName() { return "Moody Blues"; }
private final ColorUIResource primary1 =
new ColorUIResource(0x0, 0x33, 0x99);
private final ColorUIResource primary2 =
new ColorUIResource(0xD0, 0xDD, 0xFF);
private final ColorUIResource primary3 =
new ColorUIResource(0x0, 0x99, 0xFF);
private final ColorUIResource secondary1 =
new ColorUIResource(0x6F, 0x6F, 0x6F);
private final ColorUIResource secondary2 =
new ColorUIResource(0x9F, 0x9F, 0x9F);
private final ColorUIResource secondary3 =
new ColorUIResource(0x1f, 0x7f, 0xDC);
protected ColorUIResource getPrimary1() { return primary1; }
protected ColorUIResource getPrimary2() { return primary2; }
protected ColorUIResource getPrimary3() { return primary3; }
protected ColorUIResource getSecondary1() { return secondary1; }
protected ColorUIResource getSecondary2() { return secondary2; }
protected ColorUIResource getSecondary3() { return secondary3; }
}
The Moody Blues theme only changes the default primary and secondary colors of the default Java look and feel. You can also override the fonts' methods to change the fonts, as it is exemplified in the White Satin theme.
Once the user selects a particular theme from the Themes menu, an object of that particular theme's class is created and the entire UI is updated to reflect the new theme. The code for this can be written as:
MetalTheme theme = new MoodyBlueTheme();
MetalLookAndFeel.setCurrentTheme(theme);
this.setTitle(theme.getName());
try
{
UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
SwingUtilities.updateComponentTreeUI(this);
}
catch(Exception e)
{
System.out.println(e);
}
Custom Themes
Custom themes are handled by the class CustomTheme
. This class also extends the class DefaultMetalTheme
, but it significantly differs from the other built-in theme's classes. It reads the theme properties from a theme file, which is a Java property file in the specified format. Like built-in themes, this class also overrides various methods of DefaultMetalTheme
to return primary colors, secondary colors and fonts. It differs from the built-in theme's class in creation of the colors and fonts. This class reads the specified theme file, parses the colors and fonts, and then stores them appropriately. Some of the code of this class is given below.
Constructor
The constructor of CustomTheme
class initially sets colors and fonts to their default values, which are the same as that of the class DefaultMetalTheme
. Then it reads those values from the theme file's input stream.
public CustomTheme(InputStream is)
{
defaultColors();
defaultFonts();
loadProperties(is);
}
Reading the Custom Theme File
Theme file is stored as a Java properties file in the specified format. The format of this theme file is given in the "Help" of this application. Also, a sample theme file - "gray.theme" is given with this application. It is very simple and straightforward to create your own themes using this format.
The method loadProperties(InputStream is)
of CustomTheme
class reads colors and fonts from the theme file's input stream and sets values of primary colors, secondary colors and configurable fonts accordingly. If any value is missing in the theme file, default value of the DefaultMetalTheme
is used, as it is set in the constructor. Partial code of this method is given below:
private void loadProperties(InputStream is)
{
Properties prop = new Properties();
try{
prop.load(is);
}catch(IOException e)
{
System.out.println(e);
}
String szTemp = prop.getProperty("name");
if(null != szTemp)
{
szThemeName = szTemp;
}
szTemp = prop.getProperty("primary1");
if(null != szTemp)
{
primary1 = parseColor(szTemp);
}
}
Parsing Color and Font
The other two functions parseColor(String color)
and parseFont(String font)
of this class construct objects of class ColorUIResource
and FontUIResource
respectively from the string
s which are read from the theme property file.
The process of reflecting the selected theme in UI is the same for the custom theme as well, which is already discussed earlier.
Decorative Frames & Dialogs
The following lines are added in the main()
method of the application, which give decorative borders and window titles to the frames and dialogs of the application. Decorative frames and dialogs can be seen in the snapshot given at the top of this article. However, this works only with JDK 1.4.
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
If you are using JDK 1.2 or JDK 1.3, you will have to comment these two lines to compile the code without any error. No other change is required in rest of the code. I have successfully compiled and executed the code with JDK 1.2 and JDK 1.3 after commenting these two lines. Obviously, the decorated look and feel of the frames and dialogs will not be visible.
The demo available with this article has been developed with JDK 1.4 and you will need a compatible version of JVM to run it. If you are using MS Windows, you can simply run the demo by double-clicking the "theme.jar" file.
Other Resources
While developing this application, I have referred to the demo code that is provided with the JDK 1.4 kit. Here are some other resources that you might find interesting and useful:
History
- 5th July, 2002: Initial version
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.