Download Java Swing - Villanova University

Document related concepts
no text concepts found
Transcript
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


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


java.awt.Rectangle, java.awt.image.Arc, etc.
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


The Java API Documentation




See what the guys at Sun did
http://java.sun.com/j2se/downloads.html
Someone else’s look and feel source code


Always a good resource
http://java.sun.com/apis.html#j2se
The Java base API source code


ISBN 0-596-00408-7
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


Marketability



Current Look and Feels were not designed with games in mind
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.


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






Depending on the OS the user has different levels of control
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, 2nd, (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()


getID()




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 String with the name of this look and feel
isNativeLookAndFeel()


Returns a String with the ID of this look and feel
getName()


Returns a String describing this look and feel
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()



uninitialize()



Used for initializing your look and feel
Invoked by UIManager before getting the UIDefaults class from the
LookAndFeel class
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)


initComponentDefaults(UIDefaults table)



Sets the icons, borders, etc. of your look and feel
initClassDefaults(UIDefaults table)


Sets the colors of your look and feel
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”;
key_values[1] = “#FFFFFF”;
key_values[2] = “Button.margin”;
key_values[3] = new InsetsUIResource(1,1,1,1);
table.putDefaults(key_values);
//
//
//
//
1st
1st
2nd
2nd
Or
UIDefaults table …
Object[] key_values = {
/* Key
Value */
“desktop”,
“#FFFFFF”,
“Button.margin”,
new InsetsUIResource(1,1,1,1)
};
table.putDefaults(key_values);
key
key’s value
key
key’s value
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, 2nd, (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 convention the keys are named by the ComponetUI class that is
going to use it



By default all the color components are mapped to one of the System Colors
i.e. key “button.border” would be the border component used by the
ComponentUI button class
See Java Swing 2nd 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


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”



i.e. JButton.getUIClassID() returns the String “ButtonUI” and therefore the key for the UI class must be
the String“ButtonUI”
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

Pros




How

Pros


By returning a new instance
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



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


Uses less memory because there is only one instance of the ComponentUI class that is
responsible for painting
Cons


By returning a static class variable of the ComponentUI class
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






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


Registers keyboard actions to set state of 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


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)
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




Examples of Function states


Foreground color
Background color
Borders
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
import
import
import
java.awt.*;
java.io.Serializable;
java.net.URL;
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 respect 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 pressed 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


ComponentUI


A Swing class that allows for pluggable look and feel through UIManager.setLookAndFeel
Singleton


Parent class of Lightweight Java Swing classes; any Lightweight Java Swing class (see
Lightweight Java Swing Class)
Lightweight Java Swing Class


Top level swing classes; painting is partly or completely controlled by the OS
JComponent


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


A class whose sole purpose it to return instances of classes through factory methods
Factory method


Parent class of all UI classes; any UI class (see UI Class)
Factory Class


Any object or class that is use to help create the look and feel
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:


[email protected]
Homepage:

http://www.homepage.villanova.edu/william.pohlhaus