Java Swing Look and Feel

skatechildrenSoftware and s/w Development

Nov 3, 2013 (3 years and 8 months ago)

112 views

Java Swing Look and Feel

Creation of a Look and Feel
package

Introduction


This presentation is designed to show
you how to create a Java Look and
Feel for the javax.swing package.

Note Before Getting Started


You should have a basic understanding of the Java 2D API


java.awt.Graphics and java.awt.Graphics2D classes


Shape classes


java.awt.Rectangle, java.awt.image.Arc, etc.


Image classes


java.awt.Image, java.awt.images.BufferedImage, etc.


java.awt.Font and java.awt.Color


You should have a basic understanding of the Java Swing API


The various JComponents (JButton, JPanel, etc)


You are given great freedom in creating your Java look and
feel package. Therefore, you should be familiar with other
Java look and feel packages so when creating your own it
behaves in a similar manner to other Java look and feel
packages.


javax.swing.plaf.metal look and feel package


Resources to Consider


5 really good Resources


A good book on Java Swing preferably one with a chapter or two on look
and feel


Java Swing 2nd edition from O’Reilly is what I recommend


ISBN 0
-
596
-
00408
-
7


The Java API Documentation


Always a good resource


http://java.sun.com/apis.html#j2se


The Java base API source code


See what the guys at Sun did


http://java.sun.com/j2se/downloads.html


Someone else’s look and feel source code


See what someone else did, the guys at Sun tend to be more complicated
then need be


Java Developer’s forums


Get help from other developers


http://forum.java.sun.com/

Why Bother


Games


Current Look and Feels were not designed with games in mind


Marketability


Company/Software Logo, Trademark, Distinct Look and Feel


Company Standards for in house applications


Quicker Development of Applications


By splitting the look and feel from functionality you can have two
different teams developing simultaneously each working on
different aspects (functionality and look) at the same time


Easier control of look and feel


Rather then setting colors, borders, icons, etc. for each instance of
the swing components you plan on using.


Your swing applications might be running on device rather
than a PC


How Java Look and Feel
Works in a Nutshell


Each Java Swing lightweight class or JComponent has a corresponding user interface
class (UI class or ComponentUI) that is responsible for painting the component to the
graphical device.


These are controlled 100% by the user


Java Swing components that do not have a corresponding UI class are the four heavyweight
components: JFrame, JDialog, JWindow, and JApplet.


These are controlled by the Operating System and user.


Depending on the OS the user has different levels of control


The LookAndFeel class holds meta information about the look and feel


Within the LookAndFeel class the UIDefaults class associates the UI classes, which are created
by the look and feel developer, with the Lightweight Java Swing classes they are suppose to
paint


The UIManager’s setLookAndFeel methods are used to set which LookAndFeel class is
used to associate UI classes with Lightweight Java Swing classes


Once the LookAndFeel class is set any call to paint a Lightweight Java Swing class to
the graphical device will use the associated UI class


The Lightweight Java Swing class calls the UIManager’s getUI method to fetch the corresponding
UI class


The UIManager, which holds a copy of the UIDefaults class from LookAndFeel class, calls
UIDefaults class’s getUI method


UIDefaults obtains the UIClassID from the calling Lightweight Java Swing class then it gets the
corresponding UI class through reflection


Once the UI class is obtained the UI class’s static createUI method is called which returns an
instance of the UI class


The Lightweight Java Swing class then uses the UI class’s paint method when it is asked to paint
or repaint itself


See next slide fore detail UML Sequence Diagram

Detailed Sequence Diagram of
how it works


Marc Loy, Robert Eckstein, Dave Wood, James Elliott and Brain Cole,

Java Swing
, 2
nd
, (Sebastopol: O’Reilly, 2003), 1012.

Getting Started


Two basic approaches


Extending your look and feel from the javax.swing.plaf
package


Not recommended if your GUI is to run on a PC



Every UI class is abstract and therefore needs to be extended


Extending your Look and Feel from the
javax.swing.plaf.basic package or other look and feel
packages


Recommended if your GUI is to run on a PC


Has an implementation of every UI class


Only need to extend the BasicLookAndFeel class


The approach of extending from the javax.swing.plaf.basic
package is basically the same as extending from any other
look and feel package

UIManager Class


This class is responsible for setting the
look and feel


JComponents call this to obtain their
corresponding ComponentUI class for
the information on how to paint
themselves to the graphical device

LookAndFeel Class


To start, extend the BasicLookAndFeel
class


This is the class that is passed into
UIManager.setLookAndFeel method


This class is the center of your look and feel


Contains the UIDefaults class


Links JComponent classes to ComponentUI classes


Lists all the colors, icons, borders, etc. that your look and
feel as available to it


Contains all the meta information about your look and
feel


LookAndFeel Class Cont.


Abstract Methods to Implement


getDescription()


Returns a String describing this look and feel


getID()


Returns a String with the ID of this look and feel


getName()


Returns a String with the name of this look and feel


isNativeLookAndFeel()


Returns a boolean for whether or not this look and feel is native to
the operating system it is instantiated on


The Windows look and feel for Java will return true on Window
systems and false on all others


Custom look and feels will all always return false


isSupportedLookAndFeel()


Returns a boolean for whether or not this look and feel is supported
on the operating system it is instantiated on


Cross platform look and feels will usually always return true


These methods are used by the UIManager class is a variety of
ways

LookAndFeel Class Cont.


Static methods


The static methods are provided for convenience


Other methods to consider overriding


initialize()


Used for initializing your look and feel


Invoked by UIManager before getting the UIDefaults class from the
LookAndFeel class


uninitialize()


Used to uninitialize your look and feel


Invoked by UIManager before look and feel is replaced by another
look and feel


provideErrorFeedback(Component c)


Invoked when the user performs an illegal action “such as pasting
into an uneditable JTextField that has focus”


Java API

BasicLookAndFeel Class


This is the class to extend when creating
your own look and feel


By extending the BasicLookAndFeel Class you
get an implementation of every UIClass in the
javax.swing.plaf.basic package


Aside from overriding the abstract methods from
the LookAndFeel class there are only three
methods you need to override to get started


BasicLookAndFeel Class cont.


Three methods that give you complete control of your look and
feel


initSystemColorDefaults(UIDefaults table)


Sets the colors of your look and feel


initComponentDefaults(UIDefaults table)


Sets the icons, borders, etc. of your look and feel


initClassDefaults(UIDefaults table)


Sets the UI Classes corresponding to the JComponents they are
responsible for painting


By overriding these three methods you can customize your
look and feel completely


They are called before returning the UIDefaults class to the
UIManager class in the getDefaults() method of the
BasicLookAndFeel class

UIDefaults class


UIDefaults


This class holds all the information for your look
and feel


The information is stored in key/value pairs


The UIManager class retrieves Object values
from the UIDefaults class based on the key pass
to one of the many static getXXX(key) methods


Most importantly it holds the Colors, Icons,
Borders, Insets, and ComponentUI classes for
your look and feel

Note about UIDefaults class


A majority of the key/value pairs set by the BasicLookAndFeel in the
UIDefaults are only used internally in the look and feel package by
the ComponentUI classes


This is a good practice to continue because it gives you global control of
your look and feel through one class


i.e. If one wants to change the color of the buttons in the look and feel. Then
one can change the value in the UIDefaults class without having to look for it in
the button ComponentUI class


The key/value pairs that are used out of the look and feel package
are the ones that match JComponent classes to ComponentUI
classes


With this in mind two conclusions are drawn


One is obligated to set all the key/value pairs for the ComponentUI
classes


This is one reason why extending BasicLookAndFeel is a good idea because
these are all set by BasicLookAndFeel


Another is that all the Icons, Borders, Colors, etc. are controlled global through
the UIDefaults class by default in BasicLookAndFeel


One can add any type of key/value pair to the UIDefaults class to use

UIResource interface


This interface is responsible for separating user set colors, borders,
insets, etc. from the look and feel’s colors, borders, insets, etc


It has NO methods, it is used to mark objects used by the look and
feel from those objects set by the user


This allows the look and feel developer to separate those values set
by the user on the JComponent object from those set by the look and
feel.


This way the look and feel developer won’t override those values when
setting the look and feel or switching to a different look and feel or theme
(see javax.swing.plaf.metal look and feel)


Every Icon, Inset, Border, ComponentUI class, etc. for the look and
feel will have to or already has implemented this interface


There are also several convenience classes that implement this
interface that are useful


ColorUIResource, BorderUIResource, InsetUIResource, etc.


Adding key/value pairs to the
UIDefaults class


To add key/values the UIDefaults to first create an
Object[] that is the twice the length of the amount of
key/value pairs you want to add


If you are adding 12 key/value pairs the length of the
Object[] is 24


Add your key/value pairs to the Object[] by setting
the first key to the Object[] at index 0 followed by it’s
value at index 1 continue with the next key/value
pair at index 2 and 3, continue until the Object[] is
full


Then call the UIDefaults method
putDefaults(Object[] obj) and pass the Object[] to it.

Code Example of adding
Key/Value pairs to UIDefaults

/* Adding two key values to table */

UIDefaults table …

Object[] key_values = new Object[4];

key_values[0] = “desktop”;



// 1
st

key

key_values[1] = “#FFFFFF”;



// 1
st

key’s value
=
key_values[2] = “Button.margin”;



// 2
nd

key

key_values[3] = new InsetsUIResource(1,1,1,1);

// 2
nd

key’s value
=
table.putaefaults(key_valuesF;
=

Or


UIDefaults table …

Object[] key_values = {

/*

Key



Value */


“desktop”,


“#FFFFFF”,


“Button.margin”,

new InsetsUIResource(1,1,1,1)

};

table.putDefaults(key_values);

Setting System Colors


Override the initSystemColorDefaults(UIDefaults
table)


System Colors


Every color used by the BasicLookAndFeel’s UI classes is
set by default to one of the System Colors


This givens one global control of all the colors in the look
and feel


Overriding the method


Two primary ways


One


Setting every System Colors’ key/value pair with
ColorUIResource Objects


Passing the Object[] holding them in the UIDefaults table


Two


Setting some or all the System Colors’ key/value pairs
using String representations for the color values


Passing the UIDefaults class, the Object[] with the
key/value pairs to the loadSystemColors method


System colors


These are the System colors that the BasicLookAndFeel uses


desktop


Color of the desktop background


activeCaption


Color for captions (title bars) when they are active


activeCaptionText

Text color for text in captions (title bars)


activeCaptionBorder

Border color for caption (title bar) window borders


inactiveCaption


Color for captions (title bars) when not active


inactiveCaptionText

Text color for text in inactive captions (title bars)


inactiveCaptionBorder

Border color for inactive caption (title bar) window borders


window


Default color for the interior of windows


windowBorder


Color of the window border


windowText


Color of the window text


menu


Background color for menus


menuText


Text color for menus


text


Text background color


textText


Text foreground color


textHighlight


Text background color when selected


textHighlightText

Text color when selected


textInactiveText


Text color when disabled


control


Default color for controls (buttons, sliders, etc)


controlText


Default color for text in controls (buttons, sliders, etc)


controlHighlight


Highlight color for controls


controlLtHighlight

Lighter highlight color for controls


controlShadow


Shadow color for controls


controlDkShadow

Dark shadow color for controls


scrollbar


Scrollbar background (usually the "track")


info


Background color for informational text


infoText


Color for informational text


Marc Loy, Robert Eckstein, Dave Wood, James Elliott and Brain Cole,

Java Swing
, 2
nd
, (Sebastopol: O’Reilly, 2003), 1059.
and Java Source Code for javax.swing.plaf.basic.BasicLookAndFeel Sun Mircosystems Inc.

Example Code for Overriding
initSystemColorDefaults(UIDefaults table)

//By setting every System color key


protected void initSystemColorDefaults(UIDefaults table) {


ColorUIResource pr1 = new ColorUIResource(new Color(127,127,255));


ColorUIResource pr2 = new ColorUIResource(new Color(0,0,127));


ColorUIResource pr3 = new ColorUIResource(new Color(63,63,191));


ColorUIResource pr4 = new ColorUIResource(new Color(0,0,255));


ColorUIResource blk = new ColorUIResource(Color.BLACK);


ColorUIResource wht = new ColorUIResource(Color.WHITE);


ColorUIResource gry = new ColorUIResource(Color.GRAY);


Object[] colors = {


"desktop"

, pr1,
/* Color of the desktop background */


"activeCaption"


, pr3,
/* Color for captions (title bars) when they are active. */


"activeCaptionText"

, wht,
/* Text color for text in captions (title bars). */


"activeCaptionBorder"

, blk,
/* Border color for caption (title bar) window borders. */


"inactiveCaption"

, pr1,
/* Color for captions (title bars) when not active. */


"inactiveCaptionText"

, gry,
/* Text color for text in inactive captions (title bars). */


"inactiveCaptionBorder"

, gry,
/* Border color for inactive caption (title bar) window borders. */


"window"

, wht,
/* Default color for the interior of windows */


"windowBorder"

, blk,
/* Color of the window border */


"windowText"

, blk,
/* Color of the window text */


"menu"

, pr1,
/* Background color for menus */


"menuText"

, blk,
/* Text color for menus */


"text"

, pr1,
/* Text background color */


"textText"

, blk,
/* Text foreground color */


"textHighlight"

, pr4,
/* Text background color when selected */


"textHighlightText"

, wht,
/* Text color when selected */


"textInactiveText"

, gry,
/* Text color when disabled */


"control"

, pr1,
/* Default color for controls (buttons, sliders, etc) */


"controlText"

, blk,
/* Default color for text in controls (buttons, sliders, etc) */


"controlHighlight"

, pr4,
/* Highlight color for controls */


"controlLtHighlight"

, wht,
/* Lighter highlight color for controls */


"controlShadow"

, pr2,
/* Shadow color for controls */


"controlDkShadow"

, blk,
/* Dark shadow color for controls */


"scrollbar"

, pr3,
/* Scrollbar background (usually the "track") */


"info"

, wht,
/* Background color for informational text */


"infoText"

, blk
/* Color for informational text */


};


table.putDefaults(colors);


}



Example Code for Overriding
initSystemColorDefaults(UIDefaults table)

/*By setting only sum and using String representations of the
colors */


protected void initSystemColorDefaults(UIDefaults table) {


Object[] colors = {


"desktop“,



“#CC5500”,



"activeCaption“,


“#FFFFFF”,




"activeCaptionText“,

“#000000”


};


loadSystemColors(table, colors, false);


/* the last value false is saying not to override


your changes with the native system colors */





}


Setting Components


Override the initComponentDefaults(UIDefaults table)


Components are various objects used by the UI classes


Borders, Insets, Fonts, Colors, etc


By default all the color components are mapped to one of the System Colors


By convention the keys are named by the ComponetUI class that is
going to use it


i.e. key “button.border” would be the border component used by the
ComponentUI button class


See Java Swing 2
nd

Edition by O’Reilly Appendix A for a complete list of
all the components set by the BasicLookAndFeel


Overriding the method


Call the super.initComponentDefaults(UIDefaults table) method (unless you
map all the components)


This will map all the component key/value pairs from the BasicLookAndFeel


Note this is not done in initSystemColorDefaults(UIDefaults table);


Set the key/value pairs that are to be changed


Pass the Object[] holding the key/value pairs to the
UIDefaults.putDefaults(Object[] obj) method



Example Code for Overriding
initComponentDefaults(UIDefaults table)


protected void initComponentDefaults(UIDefaults table) {


super.initComponentDefaults(table);


Object[] components = {


"CheckBox.icon",

new CheckBoxIcon(),


"Button.background",pr4,


"Button.foreground",wht,


"Button.font",

new Font("Times",Font.BOLD|Font.ITALIC,10)


};


table.putDefaults(components);


}

Setting ComponentUI Classes


Override the initClassDefaults(UIDefaults table)


ComponentUI Classes are the classes responsible for painting the varies
JComponents


The keys to the ComponentUI class must match what is returned by the
JComponent’s getUIClassID() method for the class they are to paint


The convention is: the name of the JComponent minus the processing J and adding UI at
the end


i.e. JButton.getUIClassID() returns the String “ButtonUI” and therefore the key for the UI class must be
the String“ButtonUI”


The values must be a String object holding the complete name of the ComponentUI
class (this is the package+class name)


i.e. “william.swing.plaf.blue.BlueButtonUI”


The package is william.swing.plaf.blue


The class is BlueButtonUI


Overriding the method


Call the super.initClassDefaults(UIDefaults table) method (unless you map all the
ComponentUI classes)


This will map all the UI classes key/value pairs from the BasicLookAndFeel


Note this is not done in initSystemColorDefaults(UIDefaults table);


Set the key/value pairs that are to be changed


Pass the Object[] holding the key/value pairs to the UIDefaults.putDefaults(Object[] obj)
method



Example Code for Overriding
initClassDefaults(UIDefaults table)


protected void initClassDefaults(UIDefaults table) {


super.initClassDefaults(table);


//package that the ComponentUI classes belong too


String pkg = "william.swing.plaf.blue.";


Object[] classes = {


"RootPaneUI", pkg + "BlueRootPaneUI",


"PanelUI" , pkg + "BluePanelUI",


"MenuBarUI" , pkg + "BlueMenuBarUI",


"MenuUI" , pkg + "BlueMenuUI",


"ButtonUI" , pkg + "BlueButtonUI"


};


table.putDefaults(classes);


}

ComponentUI Classes


These are the classes responsible for painting the
JComponents to the graphical device


Each ComponentUI class is responsible for painting one
of the JComponents


They all extend ComponentUI class


The convention for naming the ComponentUI
Classes is the short name of the look and feel +
UIClassID of the JComponent the ComponentUI is
to paint


i.e. BlueButtonUI (Blue + ButtonUI) is responsible for for
painting JButton which returns ButtonUI as its UIClassID

ComponentUI methods


public static ComponentUI createUI(JComponent c)


The factory method that creates the UI class


Two options


One: create a singleton


How


By returning a static class variable of the ComponentUI class


Pros


Uses less memory because there is only one instance of the ComponentUI class that is
responsible for painting


Cons


No stateful information can be held for a particle JComponent but rather all the JComponets that
ComponentUI class is painting for will share the same state


Two: create a new instance


How


By returning a new instance


Pros


Stateful information may be held by the ComponentUI class of a particle JComponent and can
share state through static class variables


Cons


Uses more memory because each JComponent that needs this ComponentUI class will have an
instance of it


How to decide


More often then not you will use a singleton because any stateful information can be stored in the
JComponent itself


When the stateful information can not be stored in the JComponent and is critical to the look and feel for
that JComponent


If extending the Basic Look And Feel this must be overridden and return an
instance, singleton or new, of the ComponentUI this method is a member of


If not, then the Basic Look And Feel instance will be returned

Example of createUI method

//From william.swing.plaf.blue.BlueButtonUI

//which is responible for painting JButtons



//As a singleton.


//Note the static class variable


private static BlueButtonUI blueButtonUI = new BlueButtonUI();





public static ComponentUI createUI(JComponent c) {


return blueButtonUI;


}




Or



//As a new instance


public static ComponentUI createUI(JComponent c) {


return new BlueButtonUI();


}


ComponentUI methods cont.


public void installUI(JComponent c)


Install default components (Color, Borders, etc.)


Set the components on the JComponent if the user has not set them


To check if the user has set a component or not


Get it from the JComponent


Test if it is an instance of UIResourse


If it is not a UIResourse then the user set this value


If it is not a UIResourse


the convention is to leave what the user has set


If it is a UIResourse


Set the component to what the value is in the UIDefaults table


Use the UIManager to get values from the UIDefaults table


Examples of methods that set component state from JMenu


setForeground(Color fg)


setBackground(Color bg)


The JComponent holds the various states of the components for painting itself


Install listeners


Listeners are responsible for setting the state of the JComponent through the varies setters and getters in the
JComponent


i.e. on a mouse Pressed event of a JButton the listener will set the JButton’s pressed class variable to true through
setPressed(boolean p) method


Install Keyboard Actions


Registers keyboard actions to set state of the JComponent


i.e. registering the spacebar to be the same as the left mouse click


Install anything else you may need


More often then not this method won’t be overridden if you are extending the Basic Look And Feel
UI class


Called by JComponent when the JComponent calls setUI(ComponentUI ui)


Typically the JComponent c is casted to an object of the actual class that is to be painted


i.e. if the ComponentUI class is to paint a JSeparator then cast c to a Jseparator object

Example of installUI method

//From william.swing.plaf.blue.BlueButtonUI which is responible for painting JButtons



public void installUI(JComponent c) {


/*Casting the JComponent c to AbstractButton b (several Lightweight Java Swing class extend
AbstractButton)*/


AbstractButton b = (AbstractButton)c;




/*Setting the default components for the JButton


Getting the default font, background color and foreground color from the UIDefaults table
for buttons*/


background = UIManager.getColor("Button.background");


foreground = UIManager.getColor("Button.foreground");


font = UIManager.getFont("Button.font");



/*Setting background and foreground colors on the JComponent c if the user did NOT set them


These values can be retrieved later by the paint method through c*/


if(c.getBackground()==null || (c.getBackground() instanceof UIResource))


c.setBackground(background);


if(c.getForeground()==null || (c.getForeground() instanceof UIResource))


c.setForeground(foreground);



//Using BasicButtonUI installListeners method to install listeners


super.installListeners(b);




/*Note that there are no keyboard registations, therefore hit any of the keys will not invoke
an event*/


}

ComponentUI methods cont.


public void uninstallUI(JComponent c)


Used to remove and cleanup


Listeners


Keyboard Actions


Components


Anything else


More often then not this method won’t be
overridden if you are extending the Basic Look
And Feel UI class


Called by JComponent when the JComponent
calls setUI(ComponentUI ui) if and only if the
JComponent previously had a set it’s UI class


Example of uninstallUI method

//From javax.swing.plaf.basic.BasicButtonUI



public void uninstallUI(JComponent c) {


//Uninstalling keyboard registations


uninstallKeyboardActions((AbstractButton) c);


//Uninstalling listeners


uninstallListeners((AbstractButton) c);


//Uninstalling default components


uninstallDefaults((AbstractButton) c);


}

ComponentUI methods cont.


Sizing methods


Three sizing methods


getMinimumSize()


getMaximumSize()


getPreferredSize()


Used by various Layout Managers to get the
size of the Graphics object for the JComponent


Some will ignore these values


i.e. GridLayout and BorderLayout

ComponentUI methods cont.


public
void

paint(Graphics

g, JComponent c)


This

method

is
responsible

for painting the JComponent to
the graphical device


Most of the work will be done here


Graphical state and function state information is obtained
from the JComponent


Examples of Graphical states


Foreground color


Background color


Borders


Examples of Function states


Is the button pressed


The Graphics object is really a Graphics2D object

Example of paint method

//Example of painting semi
-
translucent lines on a MenuBar



public void paint(Graphics g, JComponent c) {


super.paint(g, c);



//Get the state of the background color from the JComponent


Color bg = c.getBackground().darker().darker();


g.setColor(new Color(bg.getRed(),bg.getGreen(),bg.getBlue(),127));



/*Added lines at every 5 pixels of 1 pixel height and the width
of the JMenuBar



Note the getting of the width and height from the JMenuBar and
that the Graphic starts at 0,0*/


for(int j=0;j<c.getHeight();) {


g.fillRect(0,j,c.getWidth(),1);


j=j+5;


}


}

Non
-
ComponentUI methods


If you are extending the Basic look and
feel you will have various methods
available to use depending on the UI
Class.

3 Ways to Control Look and
Feel


With the preceding covered, 3 ways of
controlling look and feel should
become evident by extending the
javax.swing.plaf.basic look and feel
package


Through color


Through components


Through CompnentUI classes

Through Color


This is the simplest of the three
methods


Override the
initSystemColorDefaults(UIDefaults table)
method


Set the System colors as desired


With a little trial and error one can see
quickly what System colors control what
components

Through Components


This is a bit harder than through colors but gives
greater control


Override the initComponentDefaults(UIDefaults
table) method


Set icons, borders, insets, etc. as desired


Creating your own components


Become familiar with the component you are going to create


It is easiest to create a factory class that will return instances of
your components, which will be inner classes of your factory
class (This approach is best if you have multiple types of a
particular component)


All types of a component are centrally located in one class
so the management is easier


i.e. all your borders will be inner classes of and created by
your border factory class


Example of a Checkbox Icon
Component cont.

package william.swing.plaf.blue;


import java.awt.*;

import java.io.Serializable;

import java.net.URL;

import javax.swing.*;


//I could have implemented javax.swing.plaf.IconUIResource

public class CheckBoxIcon implements UIResource, Icon, Serializable {



//two images to represent the checkbox being unchecked and checked


Image unchecked = null, checked = null;


//the size of this icon


int width = 20, height = 20;



//the constructor is loading the images



public CheckBoxIcon() {


unchecked = new
ImageIcon(this.getClass().getResource("/william/swing/plaf/blue/unchecked.gif")).getImage();


checked = new
ImageIcon(this.getClass().getResource("/william/swing/plaf/blue/checked.gif")).getImage();


}



public int getIconWidth() {


return width;


}



public int getIconHeight() {


return height;


}







Example of a Checkbox Icon
Component cont.


/*c is the checkbox, g is the Graphics object from the paint method of the CheckboxUI class


x and y are where the icon is to be painted on the Graphics object g */


public void paintIcon(Component c, Graphics g, int x, int y) {


/*This icon will be used with a JCheckBox


so the assumption can be made to cast it as an AbstractButton


will provide state information of the checkbox */


ButtonModel model = ((AbstractButton)c).getModel();


//This ant aliasing provides a nice blended into the background


((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);




//Drawing the unchecked image by default at x and y


g.drawImage(unchecked,x,y,width,height,null);




//If the checkbox is checked then paint over the unchecked image with the checked image


if(model.isSelected()||model.isPressed()||model.isArmed())


g.drawImage(checked,x,y,width,height,null);




//Drawing a 2px circle around the checkbox



g.drawOval(x+1,y+1,width
-
1,height
-
1);


g.drawOval(x,y,width,height);


}

}


Example of a Checkbox Icon
Component cont.



//Then you just add it to the UIDefaults table



protected void initComponentDefaults(UIDefaults table) {


super.initComponentDefaults(table);


Object[] components = {


"CheckBox.icon",

new CheckBoxIcon()


};


table.putDefaults(components);


}





The checkbox goes from this:


To this:





It should be noted that this would be better as a radio button then a check
box due to its round nature

Example of a Checkbox Icon
Component cont.


Two notes about using images


One


One can obtain an URL object representing the locate
of a resource relative to your class path by invoking
this.getClass().getResource(String relative_path)
method


This way one can store all images within the look and
feel package for that look and feel


Two


By using the javax.swing.ImageIcon class for retrieving
Images one is guaranteed the image will be loaded in
the Image object when getImage() method is called


This is easier and safer than use the DefaultToolkit from
the java.awt.Toolkit class for loading images

Through ComponentUI
Classes


This is the hardest but gives the greatest control


Override the initClassDefaults (UIDefaults table) method


The example is of ComponentUI responsible for painting
JButton


It is good to understand this because of the 40 or so
JComponents 8 are buttons and behave very similarly to JButton


This example shows how one has to do the following


Painting the button


Painting the text if there is text


Painting the icon if there is an icon


Setting up the listeners


Determining user values look and feel set values


A singleton


Example of a Button
ComponentUI

package william.swing.plaf.blue;


import java.awt.*;

import java.awt.image.*;

import javax.swing.*;

import java.awt.geom.*;

import javax.swing.plaf.*;

import javax.swing.plaf.basic.*;

import javax.swing.text.View;



public class BlueButtonUI extends BasicButtonUI {



//The singleton istance of BlueButtonUI


static BlueButtonUI b = new BlueButtonUI();


//Default background and foreground


Color background;


Color foreground;


//There will be only one font for this these buttons


Font font;



public BlueButtonUI() {


super();


}




//The factory method returns the singleton


public static ComponentUI createUI(JComponent c) {


return b;


}


Example of a Button
ComponentUI cont.


public void installUI(JComponent c) {


//Since we know this is a JButton it is safe to cast as an AbstractButton


AbstractButton b = (AbstractButton)c;



//Setting the default values from the UIDefaults table


background = UIManager.getColor("Button.background");


foreground = UIManager.getColor("Button.foreground");


font = UIManager.getFont("Button.font");



//Checking for user set values for foreground and background before setting them


//Note that the font compnonent is not checked therefore the value from the UIDefaults table will


//override the user’s values (This is not recommended) further not all the defaults are set


if(c.getBackground()==null || (c.getBackground() instanceof UIResource))


c.setBackground(background);



if(c.getForeground()==null || (c.getForeground() instanceof UIResource))


c.setForeground(foreground);




//Using BasicButtonUI installListeners method to install listeners


super.installListeners(b);




/*Note that there are no keyboard registations, therefore hit any of the keys will not invoke an event*/


}

Example of a Button
ComponentUI cont.

//Paints a rounded button that is semi
-
transparent with lines


public void paint(Graphics g, JComponent c) {


//Once again it is safe to cast as an AbstractButton because we know it is a JButton



AbstractButton b = (AbstractButton)c;


//The ButtonModel holds a lot of the functional state of the button


ButtonModel model = b.getModel();


//Casting to a Graphics2D for convenience, this is safew because we know that the g object is really a Graphics2D object


Graphics2D g2 = (Graphics2D)g;



//Sets the arcs widths and heights


int arc_w = (int)c.getHeight()/2;


int arc_h = arc_w;



Insets i = c.getInsets();


//Gets the area for the text and icon to be painted in with respects to the insets


Rectangle viewRect = new Rectangle(i.left,i.top,b.getWidth()
-
(i.right+i.left),b.getHeight()
-

(i.bottom + i.top));


//the area that the text will be drawn in that will be defined


//by SwingUtilities.layoutCompoundLabel


Rectangle textRect = new Rectangle(0,0,0,0);


//the area that the icon will be drawn in that will be defined


//by SwingUtilities.layoutCompoundLabel


Rectangle iconRect = new Rectangle(0,0,0,0);



//I have opted to set the base font size on the size of the button this will cause the font size to skrink or grow with resp
ect to the button size


int fontSize = (int)c.getHeight()/3;


if(fontSize<8)


fontSize = 8;


g2.setFont(new Font(font.getName(),font.getStyle(),fontSize));


//modify text for display, will add ... if clipped and


//determine the text area and icon area


String text = SwingUtilities.layoutCompoundLabel(


c, g2.getFontMetrics(), b.getText(), b.getIcon(),


b.getVerticalAlignment(), b.getHorizontalAlignment(),


b.getVerticalTextPosition(), b.getHorizontalTextPosition(),


viewRect, iconRect, textRect,



b.getText() == null ? 0 : b.getIconTextGap());


Example of a Button
ComponentUI cont.


//Starting with a BufferedImage because the graphics object from a BufferedImage respects composite overlay directives


//NOTE the Graphics object passed in to this method does not respect these directives


BufferedImage buffImg = new BufferedImage(c.getWidth(), c.getHeight(), BufferedImage.TYPE_INT_ARGB);


Graphics2D gbi = buffImg.createGraphics();


//Retrieving the state of the colors from the component which were set in the installUI method


Color back = c.getBackground();


Color fore = c.getForeground();



//creating a semi
-
transparent background for the button


Color bg = new Color(back.getRed(),back.getGreen(),back.getBlue(),127);


//Defining the color of my borders


Color wh = Color.WHITE;


Color gr = Color.GRAY;


//if button is pressed change the background to dark and switch the border colors (this makes it appear that the button is p
ressed in)


if (model.isArmed() && model.isPressed()) {


Color d = back.darker().darker().darker();


bg = new Color(d.getRed(),d.getGreen(),d.getBlue(),127);


wh = Color.GRAY;


gr = Color.WHITE;


}



//set background color


gbi.setColor(bg);


gbi.fillRoundRect(0,0,c.getWidth(),c.getHeight(),arc_w,arc_h);


//lay in the strips


gbi.setColor(Color.BLACK);


gbi.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN,1.0f));


gbi.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);


for(int j=0;j<c.getHeight();) {


gbi.fillRect(0,j,c.getWidth(),2);


j=j+4;


}



Example of a Button
ComponentUI cont.


//paint button image


g2.drawImage(buffImg,0,0,c);


//Draw borders (NOTE a better implementation would have created a borders object)


g2.setColor(wh);


g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);


g2.setStroke(new BasicStroke(2.0f));


Arc2D.Double ar1;


ar1 = new Arc2D.Double(0,0,arc_w,arc_h,90,90,Arc2D.OPEN);


g2.draw(ar1);


ar1 = new Arc2D.Double(c.getWidth()
-
arc_w,1,arc_w,arc_h,0,90,Arc2D.OPEN);


g2.draw(ar1);


g2.fillRect(arc_w/2
-
2,0,c.getWidth()
-
arc_w+2,2);


g2.fillRect(0,arc_h/2
-
2,2,c.getHeight()
-
arc_h+2);



g2.setColor(gr);


ar1 = new Arc2D.Double(c.getWidth()
-
arc_w,c.getHeight()
-
arc_h,arc_w,arc_h,270,90,Arc2D.OPEN);


g2.draw(ar1);


ar1 = new Arc2D.Double(0,c.getHeight()
-
arc_h,arc_w,arc_h,180,90,Arc2D.OPEN);


g2.draw(ar1);


g2.fillRect(c.getWidth()
-
1,arc_h/2
-
2,1,c.getHeight()
-
arc_h+8);


g2.fillRect(arc_w/2
-
8,c.getHeight()
-
2,c.getWidth()
-
arc_w+16,2);



//painting text


g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);


g2.setColor(fore);


//draw the text at the x of the textRect and the y textRect plus the font ascent.


//"The font ascent is the distance from the font's baseline to the top of most


//alphanumeric characters."(From Java API Doc on java.awt.FontMetrics.getAscent())


g2.drawString(text,(int)textRect.getX(),(int)textRect.getY()+g2.getFontMetrics().getAscent());


//If there is an icon paint it at the x and y of the iconRect


if(b.getIcon()!=null)


b.getIcon().paintIcon(c,g,(int)iconRect.getX(),(int)iconRect.getY());


}


Example of a Button
ComponentUI cont.

//Then you just add it to the UIDefaults table


protected void initClassDefaults(UIDefaults table) {


super.initClassDefaults(table);


//package that the ComponentUI classes belong too


String pkg = "william.swing.plaf.blue.";


Object[] classes = {



"ButtonUI" , pkg + "BlueButtonUI"


};


table.putDefaults(classes);


}

}

Last Note on ComponentUI


Each ComponentUI is slightly different,
though there are strong similarities
between many of them (i.e. buttons,
panels, etc.), and only through trial and
error and reviewing the source code of
the Java API or someone else’s look
and feel will you be able to fully master
each ComponentUI

Future Research


Active and Lazy Values


GTK+ for Java 1.4


How motion is achieved

Glossary for
this

Presentation


Component


Any object or class that is use to help create the look and feel


ComponentUI


Parent class of all UI classes; any UI class (see UI Class)


Factory Class


A class whose sole purpose it to return instances of classes through factory methods


Factory method


A static method that is responsible for returning and instance of a class, whether it be a singleton
or new instance


Heavyweight Java Swing Class


Top level swing classes; painting is partly or completely controlled by the OS


JComponent


Parent class of Lightweight Java Swing classes; any Lightweight Java Swing class (see
Lightweight Java Swing Class)


Lightweight Java Swing Class


A Swing class that allows for pluggable look and feel through UIManager.setLookAndFeel


Singleton


An instance of a class in which there will be only one instance within of it within the JVM


UI Class


A class that is responsible for paint a JComponent class


Author


By William Pohlhaus


Email:


william.pohlhaus@villanova.edu


Homepage:


http://www.homepage.villanova.edu/william.pohlhaus