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: 3/5 



Previous Page Previous Page (2/5) - Next Page (4/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.3  Custom L&F: part II - Creating custom UI delegates

The next step in the creation of a custom L&F is the implementation of custom UI delegates corresponding to each supported component. In this section we'll show how to implement Malachite delegates for three relatively simple Swing components: JButton, JCheckBox, and JRadioButton.

Figure 21.2 Custom Malachite UI delegates in action.

<<file figure21-2.gif>>

The Code: MalachiteLF.java

see \Chapter21\2\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

{

  // Unchanged code from section 21.1

  protected void initClassDefaults(UIDefaults table) {

    super.initClassDefaults(table);

    putDefault(table, "ButtonUI");

    putDefault(table, "CheckBoxUI");

    putDefault(table, "RadioButtonUI");

  }

  protected void putDefault(UIDefaults table, String uiKey) {

    try  {

      String className = "Malachite.Malachite"+uiKey;

      Class buttonClass = Class.forName(className);

      table.put(uiKey, className);

      table.put(className, buttonClass);

    }

    catch (Exception ex) {

      ex.printStackTrace();

    }

  }

  protected void initComponentDefaults(UIDefaults table) {

    super.initComponentDefaults(table);

    // Unchanged code from section 21.1

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

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

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

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

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

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

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

    Icon pubull = new ImageIcon("Malachite/p_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),

      "Button.focusBorder", focusBorder,

      "Button.borderPressed", borderLowered,

      "Button.activeForeground", new

        ColorUIResource(255, 255, 255),

      "Button.pressedBackground", new

        ColorUIResource(0, 96, 0),

      "CheckBox.font", commonFont,

      "CheckBox.background", commonBackground,

      "CheckBox.foreground", commonForeground,

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

      "CheckBox.focusBorder", focusBorder,

      "CheckBox.activeForeground", activeForeground,

      "CheckBox.iconPressed", new IconUIResource(pubox),

      "CheckBox.iconChecked", new IconUIResource(cbox),

      "CheckBox.iconPressedChecked", new IconUIResource(pcbox),

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

      // Unchanged code from section 21.1

      "RadioButton.font", commonFont,

      "RadioButton.background", commonBackground,

      "RadioButton.foreground", commonForeground,

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

      "RadioButton.focusBorder", focusBorder,

      "RadioButton.activeForeground", activeForeground,

      "RadioButton.iconPressed", new IconUIResource(pubull),

      "RadioButton.iconChecked", new IconUIResource(cbull),

      "RadioButton.iconPressedChecked", new IconUIResource(pcbull),

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

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

      "TextArea.border", borderLowered

    };

    table.putDefaults( defaults );

  }

}

The Code: MalachiteButtonUI.java

see \Chapter21\2\Malachite

package Malachite;

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import javax.swing.border.*;

import javax.swing.plaf.*;

import javax.swing.plaf.basic.*;

public class MalachiteButtonUI extends BasicButtonUI

 implements java.io.Serializable, MouseListener, KeyListener

{

  private final static MalachiteButtonUI m_buttonUI =

    new MalachiteButtonUI();

  protected Border m_borderRaised = null;

  protected Border m_borderLowered = null;

  protected Color  m_backgroundNormal = null;

  protected Color  m_backgroundPressed = null;

  protected Color  m_foregroundNormal = null;

  protected Color  m_foregroundActive = null;

  protected Color  m_focusBorder = null;

  public MalachiteButtonUI() {}

  public static ComponentUI createUI( JComponent c ) {

    return m_buttonUI;

  }

  public void installUI(JComponent c) {

    super.installUI(c);

    m_borderRaised = UIManager.getBorder(

      "Button.border");

    m_borderLowered = UIManager.getBorder(

      "Button.borderPressed");

    m_backgroundNormal = UIManager.getColor(

      "Button.background");

    m_backgroundPressed = UIManager.getColor(

      "Button.pressedBackground");

    m_foregroundNormal = UIManager.getColor(

      "Button.foreground");

    m_foregroundActive = UIManager.getColor(

      "Button.activeForeground");

    m_focusBorder = UIManager.getColor(

      "Button.focusBorder");

    c.addMouseListener(this);

    c.addKeyListener(this);

  }

  public void uninstallUI(JComponent c) {

    super.uninstallUI(c);

    c.removeMouseListener(this);

    c.removeKeyListener(this);

  }

  public void paint(Graphics g, JComponent c) {

    AbstractButton b = (AbstractButton) c;

    Dimension d = b.getSize();

    g.setFont(c.getFont());

    FontMetrics fm = g.getFontMetrics();

    g.setColor(b.getForeground());

    String caption = b.getText();

    int x = (d.width - fm.stringWidth(caption))/2;

    int y = (d.height + fm.getAscent())/2;

    g.drawString(caption, x, y);

    if (b.isFocusPainted() && b.hasFocus()) {

      g.setColor(m_focusBorder);

      Insets bi = b.getBorder().getBorderInsets(b);

      g.drawRect(bi.left, bi.top, d.width-bi.left-bi.right-1,

      d.height-bi.top-bi.bottom-1);

    }

  }

  public Dimension getPreferredSize(JComponent c) {

    Dimension d = super.getPreferredSize(c);

    if (m_borderRaised != null) {

      Insets ins = m_borderRaised.getBorderInsets(c);

      d.setSize(d.width+ins.left+ins.right,

        d.height+ins.top+ins.bottom);

    }

    return d;

  }

  public void mouseClicked(MouseEvent e) {}

  public void mousePressed(MouseEvent e) {

    JComponent c = (JComponent)e.getComponent();

    c.setBorder(m_borderLowered);

    c.setBackground(m_backgroundPressed);

  }

  public void mouseReleased(MouseEvent e) {

    JComponent c = (JComponent)e.getComponent();

    c.setBorder(m_borderRaised);

    c.setBackground(m_backgroundNormal);

  }

  public void mouseEntered(MouseEvent e) {

    JComponent c = (JComponent)e.getComponent();

    c.setForeground(m_foregroundActive);

    c.repaint();

  }

  public void mouseExited(MouseEvent e) {

    JComponent c = (JComponent)e.getComponent();

    c.setForeground(m_foregroundNormal);

    c.repaint();

  }

  public void keyTyped(KeyEvent e) {}

  public void keyPressed(KeyEvent e) {

    int code = e.getKeyCode();

    if (code == KeyEvent.VK_ENTER || code == KeyEvent.VK_SPACE) {

      JComponent c = (JComponent)e.getComponent();

      c.setBorder(m_borderLowered);

      c.setBackground(m_backgroundPressed);

    }

  }

  public void keyReleased(KeyEvent e) {

    int code = e.getKeyCode();

    if (code == KeyEvent.VK_ENTER || code == KeyEvent.VK_SPACE) {

      JComponent c = (JComponent)e.getComponent();

      c.setBorder(m_borderRaised);

      c.setBackground(m_backgroundNormal);

    }

  }

}

The Code: MalachiteCheckBoxUI.java

see \Chapter21\2\Malachite

package Malachite;

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import javax.swing.border.*;

import javax.swing.plaf.*;

import javax.swing.plaf.basic.*;

public class MalachiteCheckBoxUI extends BasicCheckBoxUI

 implements java.io.Serializable, MouseListener

{

  private final static MalachiteCheckBoxUI m_buttonUI =

    new MalachiteCheckBoxUI();

  protected Color  m_backgroundNormal = null;

  protected Color  m_foregroundNormal = null;

  protected Color  m_foregroundActive = null;

  protected Icon   m_checkedIcon = null;

  protected Icon   m_uncheckedIcon = null;

  protected Icon   m_pressedCheckedIcon = null;

  protected Icon   m_pressedUncheckedIcon = null;

  protected Color  m_focusBorder = null;

  protected int    m_textIconGap = -1;

  public MalachiteCheckBoxUI() {}

  public static ComponentUI createUI( JComponent c ) {

    return m_buttonUI;

  }

  public void installUI(JComponent c) {

    super.installUI(c);

    m_backgroundNormal = UIManager.getColor(

      "CheckBox.background");

    m_foregroundNormal = UIManager.getColor(

      "CheckBox.foreground");

    m_foregroundActive = UIManager.getColor(

      "CheckBox.activeForeground");

    m_checkedIcon = UIManager.getIcon(

      "CheckBox.iconChecked");

    m_uncheckedIcon = UIManager.getIcon(

      "CheckBox.icon");

    m_pressedCheckedIcon = UIManager.getIcon(

      "CheckBox.iconPressedChecked");

    m_pressedUncheckedIcon = UIManager.getIcon(

      "CheckBox.iconPressed");

    m_focusBorder = UIManager.getColor(

      "CheckBox.focusBorder");

    m_textIconGap = UIManager.getInt(

      "CheckBox.textIconGap");

    c.setBackground(m_backgroundNormal);

    c.addMouseListener(this);

  }

  public void uninstallUI(JComponent c) {

    super.uninstallUI(c);

    c.removeMouseListener(this);

  }

  public void paint(Graphics g, JComponent c) {

    AbstractButton b = (AbstractButton)c;

    ButtonModel model = b.getModel();

    Dimension d = b.getSize();

    g.setFont(c.getFont());

    FontMetrics fm = g.getFontMetrics();

    Icon icon = m_uncheckedIcon;

    if (model.isPressed() && model.isSelected())

      icon = m_pressedCheckedIcon;

    else if (model.isPressed() && !model.isSelected())

      icon = m_pressedUncheckedIcon;

    else if (!model.isPressed() && model.isSelected())

      icon = m_checkedIcon;

    g.setColor(b.getForeground());

    int x = 0;

    int y = (d.height - icon.getIconHeight())/2;

    icon.paintIcon(c, g, x, y);

    String caption = b.getText();

    x = icon.getIconWidth() + m_textIconGap;

    y = (d.height + fm.getAscent())/2;

    g.drawString(caption, x, y);

    if (b.isFocusPainted() && b.hasFocus()) {

      g.setColor(m_focusBorder);

      Insets bi = b.getBorder().getBorderInsets(b);

      g.drawRect(x-2, y-fm.getAscent()-2, d.width-x,

      fm.getAscent()+fm.getDescent()+4);

    }

  }

  public void mouseClicked(MouseEvent e) {}

  public void mousePressed(MouseEvent e) {}

  public void mouseReleased(MouseEvent e) {}

  public void mouseEntered(MouseEvent e) {

    JComponent c = (JComponent)e.getComponent();

    c.setForeground(m_foregroundActive);

    c.repaint();

  }

  public void mouseExited(MouseEvent e) {

    JComponent c = (JComponent)e.getComponent();

    c.setForeground(m_foregroundNormal);

    c.repaint();

  }

}

The Code: MalachiteRadioButtonUI.java

see \Chapter21\2\Malachite

package Malachite;

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import javax.swing.border.*;

import javax.swing.plaf.*;

import javax.swing.plaf.basic.*;

public class MalachiteRadioButtonUI extends MalachiteCheckBoxUI

 implements java.io.Serializable, MouseListener

{

  private final static MalachiteRadioButtonUI m_buttonUI =

    new MalachiteRadioButtonUI();

  public MalachiteRadioButtonUI() {}

  public static ComponentUI createUI( JComponent c ) {

    return m_buttonUI;

  }

  public void installUI(JComponent c) {

    super.installUI(c);

    m_backgroundNormal = UIManager.getColor(

      "RadioButton.background");

    m_foregroundNormal = UIManager.getColor(

      "RadioButton.foreground");

    m_foregroundActive = UIManager.getColor(

      "RadioButton.activeForeground");

    m_checkedIcon = UIManager.getIcon(

      "RadioButton.iconChecked");

    m_uncheckedIcon = UIManager.getIcon(

      "RadioButton.icon");

    m_pressedCheckedIcon = UIManager.getIcon(

      "RadioButton.iconPressedChecked");

    m_pressedUncheckedIcon = UIManager.getIcon(

      "RadioButton.iconPressed");

    m_focusBorder = UIManager.getColor(

      "RadioButton.focusBorder");

    m_textIconGap = UIManager.getInt(

      "RadioButton.textIconGap");

    c.setBackground(m_backgroundNormal);

    c.addMouseListener(this);

  }

}

Understanding the Code

Class Malachite.MalachiteLF

The initClassDefaults() method inherited from BasicLookAndFeel is now overridden. As we've discussed above, this method will be called to fill a given UIDefaults instance with information about the specific classes responsible for providing a component's UI delegate for this L&F. Our implementation calls the super-class's initClassDefaults() method to provide all default options. It then replaces the delegate classes for our three supported button components by calling our putDefault() custom method. This helper method puts two entries into the given UIDefaults instance: the UI delegate fully qualified class name, and a corresponding instance of java.lang.Class (see 21.1.11).

The initComponentDefaults() method now places more custom resources into the given UIDefaults instance, including six custom icons. These resources are needed by our custom Malachite UI delegates, as we will see below.

Class Malachite.MalachiteButtonUI

This class provides a custom UI delegate for JButton. It extends BasicButtonUI to reuse much of its functionality, and implements MouseListener and KeyListener to capture and process user input.

Class variable:

MalachiteButtonUI m_buttonUI: a shared instance of this class which is returned by createUI().

Instance variables:

Border m_borderRaised: border when not pressed.

Border m_borderLowered: border when pressed.

Color m_backgroundNormal: background color when not pressed.

Color m_backgroundPressed: background color when pressed.

Color m_foregroundNormal: foreground color.

Color m_foregroundActive: foreground color when mouse cursor rolls over.

Color m_focusBorder: focus rectangle color.

The installUI() method retrieves rendering resources from the defaults table by calling static methods defined in the UIManager class (these resources were stored by MalichiteLF as described above). It also attaches this as a MouseListener and KeyListener to the specified component. The uninstallUI() simply removes these listeners.

The paint() method renders a given component using the given graphical context. Rendering of the background and border is done automatically by JComponent (see 21.1.12), so the responsibility of this method is to simply render a button's text and focus rectangle.

The getPreferredSize() method is overridden since the default implementation in the BasicButtonUI class does not take into account the button's border (interestingly enough). Since we use a relatively thick border in Malachite, we need to override this method and add the border's insets to the width and height returned by the superclass implementation.

The next five methods represent an implementation of the MouseListener interface. To indicate that a button component is currently pressed, the mousePressed() method changes a button's background and border, which in turn causes that component to be repainted. Method mouseReleased() restores these attributes. To provide an additional rollover effect, method mouseEntered() changes the associated button's foreground color, which is then restored in the mouseExited() method.

The remaining three methods represent an implementation of the KeyListener interface. Pressing the space-bar or Enter keys while the button is in focus produces the same effect as performing a button click.

Class Malachite.MalachiteCheckBoxUI

This class extends BasicCheckBoxUI to provide a custom UI delegate for our JCheckBox component.

Class variable:

MalachiteCheckBoxUI m_buttonUI: a shared instance of this class which is returned by createUI() method.

Instance variables:

Color m_backgroundNormal: component's background.

Color m_foregroundNormal: foreground color.

Color m_foregroundActive: rollover foreground color.

Icon m_checkedIcon: icon displayed when checked and not pressed.

Icon m_uncheckedIcon: icon displayed when not checked and not pressed.

Icon m_pressedCheckedIcon: icon displayed when checked and pressed.

Icon m_pressedUncheckedIcon: icon displayed when not checked and pressed.

Color m_focusBorder:focus rectangle color.

int m_textIconGap: gap between icon and text.

Similar to MalachiteButtonUI, the installUI() method retrieves rendering resources from the defaults table and stores them in instance variables. It also attatches this as a MouseListener to the given component.

The paint() method renders the given component using a given graphical context. It draws an icon, text, and focus rectangle when appropriate (this code is fairly straightforward and does not require detailed explanation here).

The next five methods represent an implementation of the MouseListener interface which provides a similar rollover effect to that of MalachiteButtonUI.

Class Malachite.MalachiteRadioButtonUI

This class extends MalachiteCheckBoxUI. The only major difference between this class and its parent is that this class uses a different set of icons to render the radio button. The paint() method is not overriden. The installUI() method is modified to retrieve the necessary resources.

Running the Code

Figure 21.2 shows our example frame from the previous section with our new Malachite UI delegates in action. You can see that the push buttons have became bigger because their size now properly includes the border thickness. The most significant difference appears when the buttons are clicked, and boxes are checked/unchecked using using the mouse and keyboard.

At this point we leave the implementation of Malachite UI delegates for other existing Swing components up to you. You should now have a good idea of how to approach the task for any component. Switching gears, we will now discuss L&F customization from the opposite point of view: providing existing L&F capabilities for custom components.



[ 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