Friday, 5 February 2016

Java look & feel themes

Themes for your Swing application
<!------------------------------- STEP 3 ---------------------------><!-- Add the article text. Please use simple formatting (

,
etc) -->

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 classMetalTheme. The class DefaultMetalTheme overrides all the required methods, which give the Javalook 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 DefaultMetalThemeclass, 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 ofDefaultMetalTheme.

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 classDefaultMetalTheme. 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"; }
    
          // blue shades
    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);
    
          // the functions overridden from the base 
          // class => DefaultMetalTheme
    
    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 Javalook 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 -
   // user selects theme - Moody Blues 
MetalTheme theme = new MoodyBlueTheme();  
   // set the chosen theme
MetalLookAndFeel.setCurrentTheme(theme);
   // Show name of the theme as window title
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 classDefaultMetalTheme, 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 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();
        // load the Properties
    try{
          prop.load(is);
    }catch(IOException e)
    {
          System.out.println(e);    
    }
        // get theme name
    String szTemp = prop.getProperty("name");
    if(null != szTemp)
    {
          szThemeName = szTemp;
    }
    
        // get primary colors    
    szTemp = prop.getProperty("primary1");
    if(null != szTemp)
    {
          primary1 = parseColor(szTemp);
    }
    
        // read more properties
        // ....................................
}

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 strings which are read from the theme property file.
The process of reflecting the selected theme in UI is 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.
    // For JDK 1.4 only. 
    // Comment these lines for JDK 1.2 & 1.3
    
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 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.

No comments:

Post a Comment