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) ]




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