Easy to Learn Java: Programming Articles, Examples and Tips

Start with Java in a few days with Java Lessons or Lectures

Home

Code Examples

Java Tools

More Java Tools!

Java Forum

All Java Tips

Books

Submit News
Search the site here...
Search...
 

Swing Chapter 21. (Advanced topics) Pluggable Look & Feel. Easy for reading, Click here!

Custom Search
Swing Chapter 21. (Advanced topics) Pluggable Look & Feel. Easy for reading, Click here!

[ Return to Swing (Book) ]

Page: 2/5 



Previous Page Previous Page (1/5) - Next Page (3/5) Next Page
Subpages: 1. Pluggable Look & Feel overview
2. Custom L&F: part I - Using custom resources
3. Custom L&F: part II - Creating custom UI delegates
4. L&F for custom components: part I - Implementing L&F support
5. L&F for custom components: part II - Third party L&F support

21.2  Custom L&F: part I - Using custom resources

UI Guideline : When to consider a Custom L&F

Developing a Custom L&F is not a trivial undertaking. Almost certainly, there is more effort needed for the design rather than the coding. Consider a Custom Look & Feel in these situations:

You are designing a single use system, such as a self-service kiosk

You are intending to roll-out a suite of enterprise applications which will work together and you want the L&F to reflect the corporate image or identity.

You are developing a family of software products and want to develop a unique environment or corporate identity. This was exactly Sun's intention with Metal L&F which closely reflects the colours and styles used in the Sun Corporate identity. Other examples of custom designed environments are Lotus Notes, Lotus eSuite and Sun HotJava Views.

The easiest way to create a custom look-and-feel is to simply customize default component resources (colors, borders, fonts, etc.) without actually implementing any custom UI delegates. In this case, the only thing we need to do is extend BasicLookAndFeel (see above discussion), or another existing LookAndFeel implementation, and provide a set of resources. The following example demonstrates how this can be done by beginning the implementation of our custom "Malachite" L&F.

Figure 21.1 Malachite L&F in action.

<<file figure21-1.gif>>

The Code: Button1.java

see \Chapter21\1

import java.awt.*;

import java.awt.event.*;

import java.util.*;

import javax.swing.*;

import javax.swing.event.*;

import Malachite.*;

public class Button1 extends JFrame

{

  protected Hashtable  m_lfs;

  public Button1() {

    super("Look and Feel [Resources]");

    setSize(400, 300);

    getContentPane().setLayout(new FlowLayout());

    JMenuBar menuBar = createMenuBar();

    setJMenuBar(menuBar);

    JPanel p = new JPanel();

    JButton bt1 = new JButton("Click Me");

    p.add(bt1);

    JButton bt2 = new JButton("Don't Touch Me");

    p.add(bt2);

    getContentPane().add(p);

    p = new JPanel();

    JCheckBox chk1 = new JCheckBox("I'm checked");

    chk1.setSelected(true);

    p.add(chk1);

    JCheckBox chk2 = new JCheckBox("I'm unchecked");

    chk2.setSelected(false);

    p.add(chk2);

    getContentPane().add(p);

    p = new JPanel();

    ButtonGroup grp = new ButtonGroup();

    JRadioButton rd1 = new JRadioButton("Option 1");

    rd1.setSelected(true);

    p.add(rd1);

    grp.add(rd1);

    JRadioButton rd2 = new JRadioButton("Option 2");

    p.add(rd2);

    grp.add(rd2);

    JRadioButton rd3 = new JRadioButton("Option 3");

    p.add(rd3);

    grp.add(rd3);

    getContentPane().add(p);

    JTextArea txt = new JTextArea(5, 30);

    getContentPane().add(txt);

    WindowListener wndCloser = new WindowAdapter() {

      public void windowClosing(WindowEvent e) {

        System.exit(0);

      }

    };

    addWindowListener(wndCloser);

    setVisible(true);

  }

  protected JMenuBar createMenuBar() {

    JMenuBar menuBar = new JMenuBar();

    JMenu mFile = new JMenu("File");

    mFile.setMnemonic('f');

    JMenuItem mItem = new JMenuItem("Exit");

    mItem.setMnemonic('x');

    ActionListener lstExit = new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        System.exit(0);

      }

    };

    mItem.addActionListener(lstExit);

    mFile.add(mItem);

    menuBar.add(mFile);

    ActionListener lst = new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        String str = e.getActionCommand();

        Object obj = m_lfs.get(str);

        if (obj != null)

        try {

          String className = (String)obj;

          Class lnfClass = Class.forName(className);

          UIManager.setLookAndFeel(

            (LookAndFeel)(lnfClass.newInstance()));

          SwingUtilities.updateComponentTreeUI(

            Button1.this);

        }

        catch (Exception ex) {

          ex.printStackTrace();

          System.err.println(ex.toString());

        }

      }

    };

    m_lfs = new Hashtable();

    UIManager.LookAndFeelInfo lfs[] =

      UIManager.getInstalledLookAndFeels();

    JMenu mLF = new JMenu("Look&Feel");

    mLF.setMnemonic('l');

    for (int k = 0; k < lfs.length; k++ ) {

      String name = lfs[k].getName();

      JMenuItem lf = new JMenuItem(name);

      m_lfs.put(name, lfs[k].getClassName());

      lf.addActionListener(lst);

      mLF.add(lf);

    }

    menuBar.add(mLF);

    return menuBar;

  }

  public static void main(String argv[]) {

    try {

      LookAndFeel malachite = new Malachite.MalachiteLF();

      UIManager.LookAndFeelInfo info =

        new UIManager.LookAndFeelInfo(malachite.getName(),

      malachite.getClass().getName());

      UIManager.installLookAndFeel(info);

      UIManager.setLookAndFeel(malachite);

    }

    catch (Exception ex) {

      ex.printStackTrace();

      System.err.println(ex.toString());

    }

    new Button1();

  }

}

The Code: MalachiteLF.java

see \Chapter21\1\Malachite

package Malachite;

import java.awt.*;

import javax.swing.*;

import javax.swing.plaf.*;

import javax.swing.plaf.basic.*;

public class MalachiteLF extends BasicLookAndFeel

 implements java.io.Serializable

{

  public String getID() { return "Malachite"; }

  public String getName() { return "Malachite Look and Feel"; }

  public String getDescription() { return "Sample L&F from Swing"; }

  public boolean isNativeLookAndFeel() { return false; }

  public boolean isSupportedLookAndFeel() { return true; }

  protected void initComponentDefaults(UIDefaults table) {

    super.initComponentDefaults(table);

    ColorUIResource commonBackground =

      new ColorUIResource(152, 208, 128);

    ColorUIResource commonForeground =

      new ColorUIResource(0, 0, 0);

    ColorUIResource buttonBackground =

      new ColorUIResource(4, 108, 2);

    ColorUIResource buttonForeground =

      new ColorUIResource(236, 236, 0);

    ColorUIResource menuBackground =

      new ColorUIResource(128, 192, 128);

    BorderUIResource borderRaised = new

      BorderUIResource(new MalachiteBorder(

    MalachiteBorder.RAISED));

    BorderUIResource borderLowered = new

      BorderUIResource(new MalachiteBorder(

        MalachiteBorder.LOWERED));

    FontUIResource commonFont = new

      FontUIResource("Arial", Font.BOLD, 12 );

    Icon ubox = new ImageIcon("Malachite/ubox.gif");

    Icon ubull = new ImageIcon("Malachite/ubull.gif");

    Object[] defaults = {

      "Button.font", commonFont,

      "Button.background", buttonBackground,

      "Button.foreground", buttonForeground,

      "Button.border", borderRaised,

      "Button.margin", new InsetsUIResource(8, 8, 8, 8),

      "Button.textIconGap", new Integer(4),

      "Button.textShiftOffset", new Integer(2),

      "CheckBox.font", commonFont,

      "CheckBox.background", commonBackground,

      "CheckBox.foreground", commonForeground,

      "CheckBox.icon", new IconUIResource(ubox),

      "MenuBar.font", commonFont,

      "MenuBar.background", menuBackground,

      "MenuBar.foreground", commonForeground,

      "Menu.font", commonFont,

      "Menu.background", menuBackground,

      "Menu.foreground", commonForeground,

      "Menu.selectionBackground", buttonBackground,

      "Menu.selectionForeground", buttonForeground,

      "MenuItem.font", commonFont,

      "MenuItem.background", menuBackground,

      "MenuItem.foreground", commonForeground,

      "MenuItem.selectionBackground", buttonBackground,

      "MenuItem.selectionForeground", buttonForeground,

      "MenuItem.margin", new InsetsUIResource(2, 2, 2, 2),

      "Panel.background", commonBackground,

      "Panel.foreground", commonForeground,

      "RadioButton.font", commonFont,

      "RadioButton.background", commonBackground,

      "RadioButton.foreground", commonForeground,

      "RadioButton.icon", new IconUIResource(ubull),

      "TextArea.margin", new InsetsUIResource(8, 8, 8, 8),

      "TextArea.border", borderLowered

    };

    table.putDefaults( defaults );

  }

}

The Code: MalachiteBorder.java

see \Chapter21\1\Malachite

package Malachite;

import java.awt.*;

import javax.swing.*;

import javax.swing.border.*;

import javax.swing.event.*;

public class MalachiteBorder implements Border

{

  public static final int RAISED = 0;

  public static final int LOWERED = 1;

  static final String IMAGE_DIR = "Malachite/";

  static final ImageIcon IMAGE_NW = new ImageIcon(

    IMAGE_DIR+"nw.gif");

  static final ImageIcon IMAGE_N  = new ImageIcon(

    IMAGE_DIR+"n.gif");

  static final ImageIcon IMAGE_NE = new ImageIcon(

    IMAGE_DIR+"ne.gif");

  static final ImageIcon IMAGE_E  = new ImageIcon(

    IMAGE_DIR+"e.gif");

  static final ImageIcon IMAGE_SE = new ImageIcon(

    IMAGE_DIR+"se.gif");

  static final ImageIcon IMAGE_S  = new ImageIcon(

    IMAGE_DIR+"s.gif");

  static final ImageIcon IMAGE_SW = new ImageIcon(

    IMAGE_DIR+"sw.gif");

  static final ImageIcon IMAGE_W  = new ImageIcon(

    IMAGE_DIR+"w.gif");

  static final ImageIcon IMAGE_L_NW = new ImageIcon(

    IMAGE_DIR+"l_nw.gif");

  static final ImageIcon IMAGE_L_N  = new ImageIcon(

    IMAGE_DIR+"l_n.gif");

  static final ImageIcon IMAGE_L_NE = new ImageIcon(

    IMAGE_DIR+"l_ne.gif");

  static final ImageIcon IMAGE_L_E  = new ImageIcon(

    IMAGE_DIR+"l_e.gif");

  static final ImageIcon IMAGE_L_SE = new ImageIcon(

    IMAGE_DIR+"l_se.gif");

  static final ImageIcon IMAGE_L_S  = new ImageIcon(

    IMAGE_DIR+"l_s.gif");

  static final ImageIcon IMAGE_L_SW = new ImageIcon(

    IMAGE_DIR+"l_sw.gif");

  static final ImageIcon IMAGE_L_W  = new ImageIcon(

    IMAGE_DIR+"l_w.gif");

  protected int m_w = 7;

  protected int m_h = 7;

  protected boolean m_isRaised = true;

  public MalachiteBorder() {}

  public MalachiteBorder(int type) {

    if (type != RAISED && type != LOWERED)

      throw new IllegalArgumentException(

        "Type must be RAISED or LOWERED");

    m_isRaised = (type == RAISED);

  }

  public Insets getBorderInsets(Component c) {

    return new Insets(m_h, m_w, m_h, m_w);

  }

  public  boolean isBorderOpaque() { return true; }

  public void paintBorder(Component c, Graphics g,

   int x, int y, int w, int h)

  {

    int x1 = x+m_w;

    int x2 = x+w-m_w;

    int y1 = y+m_h;

    int y2 = y+h-m_h;

    int xx, yy;

    if (m_isRaised) {

      for (xx=x1; xx<=x2; xx += IMAGE_N.getIconWidth())

        g.drawImage(IMAGE_N.getImage(), xx, y, c);

      for (yy=y1; yy<=y2; yy += IMAGE_E.getIconHeight())

        g.drawImage(IMAGE_E.getImage(), x2, yy, c);

      for (xx=x1; xx<=x2; xx += IMAGE_S.getIconWidth())

        g.drawImage(IMAGE_S.getImage(), xx, y2, c);

      for (yy=y1; yy<=y2; yy += IMAGE_W.getIconHeight())

        g.drawImage(IMAGE_W.getImage(), x, yy, c);

      g.drawImage(IMAGE_NW.getImage(), x, y, c);

      g.drawImage(IMAGE_NE.getImage(), x2, y, c);

      g.drawImage(IMAGE_SE.getImage(), x2, y2, c);

      g.drawImage(IMAGE_SW.getImage(), x, y2, c);

    }

    else {

      for (xx=x1; xx<=x2; xx += IMAGE_L_N.getIconWidth())

        g.drawImage(IMAGE_L_N.getImage(), xx, y, c);

      for (yy=y1; yy<=y2; yy += IMAGE_L_E.getIconHeight())

        g.drawImage(IMAGE_L_E.getImage(), x2, yy, c);

      for (xx=x1; xx<=x2; xx += IMAGE_L_S.getIconWidth())

        g.drawImage(IMAGE_L_S.getImage(), xx, y2, c);

      for (yy=y1; yy<=y2; yy += IMAGE_L_W.getIconHeight())

        g.drawImage(IMAGE_L_W.getImage(), x, yy, c);

      g.drawImage(IMAGE_L_NW.getImage(), x, y, c);

      g.drawImage(IMAGE_L_NE.getImage(), x2, y, c);

      g.drawImage(IMAGE_L_SE.getImage(), x2, y2, c);

      g.drawImage(IMAGE_L_SW.getImage(), x, y2, c);

    }

  }

}

Understanding the Code

Class Button1

This class represents a simple frame container populated by several components: JButtons, JCheckBoxes, JRadioButtons, and JTextArea. Code in the constructor should be familair and requires no special explanation here.

The createMenuBar() method is responsible for creating this frame's menu bar. A menu titled "Look&Feel" is populated by menu items corresponding to LookAndFeel implementations available on the current JVM. An array of UIManager.LookAndFeelInfo instances is retrieved using the UIManager.getInstalledLookAndFeels() method. L&F class names stored in each info object are placed into the m_lfs Hashtable for future use. A brief text description of a particular L&F retrieved using the getName() method is used to create each corresponding menu item.

When a menu item is selected, the corresponding ActionListener updates the L&F for our application. This listener locates the class name corresponding to the selected menu item, and a new instance of that class is created, through reflection, and set as the current L&F using the UIManager.setLookAndFeel() method.

Note: We can also use the overloaded UIManager.setLookAndFeel(String className) method which takes a fully qualified LookAndFeel class name as parameter. However, as of Java 2 FCS, this does not work properly on all platforms.

The main() method creates an instance of our custom L&F, MalachiteLF (defined in the Malachite package), makes it available to the the Java session using UIManager.installLookAndFeel(), and sets is as the current L&F using UIManager.setLookAndFeel(). Our example frame is then created, which uses Malachite resources initially.

Class Malachite.MalachiteLF

This class defines our Malachite look-and-feel. Note that it extends BasicLookAndFeel to override its functionality and resources only where necessary. This L&F is centered around a green malachite palette.

Note: Malachite is a green mineral containing copper. This mineral can be found in the Ural Mountains of Russia, in Australia, and in Arizona in the US. Since ancient times it has been used as gemstone.

Methods getID(), getName(), and getDescription() return a short ID, name, and a text description of this L&F respectively. As we've discussed earlier, method initComponentDefaults() fills a given UIDefaults instance with key/value pairs representing information specific to this L&F. In our implementation we customize resources for the following components (recall that the "J" prefix is not used): Button, CheckBox, RadioButton, TextArea, MenuBar, Menu, MenuItem, and Panel.

Note that we did not define the initClassDefaults() method because we have not implemented any custom UI delegates (we will do this in the next section).

Class Malachite.MalachiteBorder

This class defines our custom Malachite implementation of the Border interface. This border is intended to provide the illusion of a 3D frame cut out of a green gemstone. It can be drawn in two forms: lowered or raised. A 3D effect is produced through the proper combination of previously prepared images. The actual rendering is done in the paintBorder() method, which simply draws a set of these images to render the border.

Running the Code

Figure 21.1 shows our Button1 example frame populated with controls using the Malachite L&F. Note that these controls are lifeless. We cannot click buttons, check or uncheck boxes, or select radio buttons. Try using the menu to select another L&F available on your system and note the differences.

The components are actually fully functional when using the Malachite L&F, but they do not have the ability to change their appearance in response to user interaction. More functionality needs to be added to provide mouse and key listener capabilities, as well as additional resources for use in representing the selected state of the button components. We will do this in the next section.

Note: The UI delegate used for each of these components is the corresponding basic L&F version, because we did not override any class defaults in MalachiteLF. A quick look in the source code for these delegates shows that the rendering functionality for selected and focused states is not implemented. All sub-classes corresponding to specific L&Fs are responsible for implementing this functionality themselves.

Note: The text area in this example is not placed in a scrolling pane specifically because we want to emphasize the use of our custom border. This is the reason it resizes when a significant amount of text is entered.



[ Return to Swing (Book) ]


Top 10 read Java Articles
 Get free "1000 Java Tips eBook"

 Java Calendar and Date: good to know facts and code examples

 Array vs ArrayList vs LinkedList vs Vector: an excellent overview and examples

 How can I convert any Java Object into byte array? And byte array to file object

 The Java Lesson 1: What is Java?

 How do I compare two dates and times, date between dates, time between times and

 Maven vs Ant or Ant vs Maven?

 How to open, read, write, close file(s) in Java? Examples on move, rename and de

 Java Array

 Java: JLabel font and color


[ More in News Section ]
Java Lessons

The Java Lesson 1:
What is Java?
The Java Lesson 2:
Anatomy of a simple Java program
The Java Lesson 3:
Identifiers and primitive data types
The Java Lesson 4:
Variables, constants, and literals
The Java Lesson 5:
Arithmetic operations, conversions, and casts
The Java Lesson 6:
Boolean expressions and operations
The Java Lesson 7:
Bitwise operations
The Java Lesson 8:
Flow control with if and else
The Java Lesson 9:
switch statements
The Java Lesson 10:
for, while, and do-while statements
The Java Lesson 11:
Using break and continue
The Java Lesson 12:
Class methods and how they are called
The Java Lesson 13:
Using the Math class
The Java Lesson 14:
Creating and calling custom class methods
The Java Lesson 15:
Overloading class methods
The Java Lesson 16:
An introduction to objects and object references
The Java Lesson 17:
The String class
The Java Lesson 18:
The StringBuffer class
The Java Lesson 19:
Initializing and processing arrays of primitives
The Java Lesson 20:
Initializing and processing arrays of objects
The Java Lesson 23:
Inheritance and overriding inherited methods
The Java Lesson 24:
abstract classes and polymorphism
The Java Lesson 25:
Interfaces, instanceof, and object conversion and casting
The Java Lesson 26:
Introduction to graphical programming and the java.awt packa
The Java Lesson 27:
The Component class
The Java Lesson 28:
Containers and simple layout managers
The Java Lesson 29:
The Color and Font classes
The Java Lesson 30:
Drawing geometric shapes
The Java Lesson 31:
Choice, List, and Checkbox controls
The Java Lesson 32:
Using the Scrollbar graphical control
The Java Lesson 33:
Menus and submenus
The Java Lesson 34:
An introduction to applets and the Applet class
The Java Lesson 35:
Essential HTML to launch an applet and pass it parameters
The Java Lesson 36:
Mouse event processing
Java Lesson 37:
Menus and submenus
Java Lesson 38:
The WindowListener interface and the WindowAdapter class
Java Lesson 39:
An introduction to GridBagLayout
Java Lesson 40:
An introduction to the Java Collections API
Java Lesson 41:
Exception handling with try, catch, and finally blocks
Java Lesson 42:
Claiming and throwing exceptions
Java Lesson 43:
Multithreading, the Thread class, and the Runnable interface
Java Lesson 44:
An introduction to I/O and the File and FileDialog classes
Java Lesson 45:
Low-level and high-level stream classes
Java Lesson 46:
Using the RandomAccessFile class
Java Lessons by
Joh Huhtala: Update

Latest articles
 Java Profiler JProbe to Resolve Performance Problems Faster

 SSL with GlassFish v2, page 5

 SSL with GlassFish v2, page 4

 SSL with GlassFish v2, page 3

 SSL with GlassFish v2, page 2

 The Java Lesson 2: Anatomy of a simple Java program, page 2

 New site about Java for robots and robotics: both software and hardware.

 Exceptions -III: What's an exception and why do I care?

 Exceptions -II: What's an exception and why do I care?

 Exceptions: What's an exception and why do I care?

 Double your Java code quality in 10 minutes, here is receipt

 Murach's Java Servlets and JSP

 How to get ascii code from a char in Java?

 Can we just try without catch? Yes!

 Make Tomcat page load faster

 Make your Tomcat More secure - limit network address for certain IP addresses

 New Java book online starts now here...

 Implementing RESTful Web Services in Java

 Firefox trimming from 1 GB to 40 Mb with many tabs opened

 SSL with GlassFish v2

 My request to replublish Tech Tips

 Search JavaFAQ.nu site here

 New Advanced Installer for Java 6.0 brings XML updates and imports 3rd party MSI

 EJB programming restrictions

 Maven vs Ant or Ant vs Maven?

 Why Java does not use default value which it should?

 How to unsign signed bytes in Java - your guide is here

 The Java Lesson 3: Identifiers and primitive data types. Page 2

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 4

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 3


[ More in News Section ]


Home Code Examples Java Forum All Java Tips Books Submit News, Code... Search... Offshore Software Tech Doodling

RSS feed Java FAQ RSS feed Java FAQ News     

    RSS feed Java Forums RSS feed Java Forums

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest 1999-2006 by Java FAQs Daily Tips.

Interactive software released under GNU GPL, Code Credits, Privacy Policy