Download Java Look and Feel

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
William Pohlhaus
Introduction ......................................................................................................................... 2
Overview of Look and Feel ................................................................................................ 3
Look and Feel Foundation Classes ..................................................................................... 5
UIManager class ............................................................................................................. 5
LookAndFeel class.......................................................................................................... 6
UIDefaults class .............................................................................................................. 7
UIResource interface ...................................................................................................... 8
Basic Look and Feel.......................................................................................................... 10
Through Color ............................................................................................................... 10
Through Components.................................................................................................... 12
Through UI Classes....................................................................................................... 14
UI Class ............................................................................................................................. 15
Methods......................................................................................................................... 16
Conclusion ........................................................................................................................ 18
Appendix A: UML Sequence Diagram............................................................................. 19
Appendix B: System Color Reference .............................................................................. 20
Appendix C: BlueButtonUI class...................................................................................... 21
Appendix D: Resources .................................................................................................... 24
Java Look and Feel
1
William Pohlhaus
Java Look and Feel
Introduction
Java Swing allows for the creations for of an independent look and feel of the
graphical user interface (GUI) from the functionality of that GUI. This allows for great
freedom for development of GUIs and the look and feel of the GUI. It provides a way for
quicker application development by separating the look and feel from the functionality,
because two teams can work simultaneously on a project with little interaction.
Note:
The reader of this paper should have an understanding of Java Swing and the Java 2D API
before proceeding. Understand, also, that the reader should reference both the Java J2SE
API and PowerPoint presentation for this paper.
Of the many questions that might come up when thinking about customizing a look
and feel the most important is “why bother?” There are several answers to this question
depending on the context that the developer faces. If the developer is a game developer
then the answer is simple; the designs of the current look and feels available are not good
for games. A good reason to bother is marketability of the application. A company wants
their applications to stand out from the rest. Customizing a look and feel allows for
distinct look and feels that may include company logos and trademarks. Moreover, the
application might not be running on a personal computer and therefore the only option is
a customized look and feel because the standard ones are for personal computers.
Java Look and Feel
2
William Pohlhaus
Overview of Look and Feel
In order to understand how to make a pluggable look and feel package, the look and
feel developer must, first, understand how Java accomplishes look and feel for its Swing
components. Java separates Swing into two set of classes: lightweight and heavyweight.
The heavyweight classes delegate painting to the operating system with some user input
depending on how much control the operating system hands over to the user. These
classes consist of all the top level classes: JFrame, JDialog, JWindow, and JApplet. The
other set of classes are the Java Swing lightweight classes which all inherit from
JComponent and delegate the painting to a separate UI class. When a lightweight Swing
class is instantiated, it calls the UIManager to retrieve the UI class that is responsible for
painting that particular class to the graphical device from the UIDefaults class. The
UIManager gets the UIDefaults class from the LookAndFeel class that is set by the
setLookAndFeel method. While much of this seems confusing, it isn’t. Especially, once
an understanding of all the individual pieces is accomplished. Appendix A shows a UML
sequence diagram of this.
To get started on creating a pluggable look and feel, the look and feel developer
must decide between two design methods of creating a look and feel in Java. One is to
create the look and feel by extending the javax.swing.plaf package the other is to extend
an existing look and feel package, usually javax.swing.plaf.basic. It not recommended
extending a look and feel from the javax.swing.plaf if the look and feel is going to be for
a personal computer. This is because the javax.swing.basic package has extended almost
the entirety of the javax.swing.plaf package for the developer to use. This allows the look
Java Look and Feel
3
William Pohlhaus
and feel developer to pick and choose which things about the look and feel to customize
without having to extended and implement everything. Further, in its implementation of
the java.swing.plaf package a basic principle is followed that allows for customizing a
look and feel very easily. This principle is the centralization of components, color and UI
classes within the LookAndFeel class. And finally, the javax.swing.plaf.basic package
paints the lightweight Swing components in expected ways. However, the
recommendation changes if the look and feel developer is creating a look and feel for a
device other than the computer screen. Then the preferred method is extending the
javax.swing.plaf package from scratch.
There are some foundation classes to look and feel in Java that the look and feel
developer must be aware of and understand, with respect to look and feel, before the
developer can understand the methodologies for customizing look and feel in Java. These
classes are the UIManager class, the LookAndFeel class, the UIDefaults class, the
UIResource interface and the UI classes. With a mastery of these classes the look and feel
developer can customize the look and feel any way s/he wants.
Java Look and Feel
4
William Pohlhaus
Look and Feel Foundation Classes
UIManager class
The UIManager class is responsible for setting and changing the pluggable look and
feel for the lightweight Swing classes through its setLookAndFeel method. To set a
pluggable look and feel the developer calls on the UIManager.setLookAndFeel static
method. Passed to the method is a Sting argument or an instance of a LookAndFeel class.
If it is a String argument that String should be the fully qualified name of the
LookAndFeel class for the pluggable look and feel. If the developer was setting the Metal
look and feel s/he would pass either the String
“javax.swing.plaf.metal.MetalLookAndFeel” or new
javax.swing.plaf.metal.MetalLookAndFeel(). In order to switch the pluggable look and
feel the developer sets the new look and feel and then calls the
javax.swing.SwingUtilities’s method updateComponentTreeUI(Component c) on the top
level Swing components, the components that are holding all the other Swing
components. These are usually one of the heavyweight Swing class, JWindow, JFrame,
JApplet or JDialog. The reason for this is that each lightweight Swing class needs to
update its UI class. The updateComponentTreeUI(Component c) notifies the lightweight
Swing classes to allow for just that. The UIManager class is, also responsible for
retrieving the UI classes from the pluggable look and feel, through its static method
getUI(JCompnent c), and setting those UI classes for the lightweight Swing classes. This
is achieved by retrieving the UIDefaults class from the LookAndFeel class and getting
the UI class from the UIDefaults class.
The following code switches a look and feel from Blue to Metal:
UIManager.setLookAndFeel(new William.swing.plaf.blue.BlueLookAndFeel());
Java Look and Feel
5
William Pohlhaus
JFrame f = new JFrame();
…
UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
SwingUtilities.updateComponentTreeUI(f);
LookAndFeel class
The center of the look and feel is the LookAndFeel class. This class holds all the meta
information and sets all the customization of the look and feel to the UIDefaults class.
There are five abstract methods that must be implemented: getDescription(), getID(),
getName(), isNativeLookAndFeel() and isSupportedLookAndFeel(). The
getDescription() method will return a description of the look and feel. The getID()
method will return the ID for the look and feel that applications and services can use to
identify it. The getName() method will return the name of the look and feel. The Java
API notes that if the developer is extending an existing look and feel, such as Motif,
Metal, Windows or Mac, but “doesn't make any fundamental changes” (Java API) then
getID() should not be modified but getName() should note a difference to identify the
extended look and feel from the parent look and feel. For example if the developer
extended Metal in such a way then the getID() would still return “Metal” but the
getName() might return “Blue Metal”. The isNativeLookAndFeel() method should return
true if the look and feel is the native look and feel for that operating system, i.e. the
Windows look and feel returns true on Windows machines. The
isSupportedLookAndFeel() method will return true if the underlying operating system
supports this look and feel. It usually will only return false if there are legal reasons.
Cross platform look and feels will usually always return true.
The Metal look and feel returns the following meta information on a Windows machine:
Name:
Metal
ID:
Metal
Description: The Java(tm) Look and Feel
Is Native:
false
Is Supported: true
Java Look and Feel
6
William Pohlhaus
There are three other methods that the developer might want to consider
overriding: initialize(), uninitialize() and provideErrorFeedback(Component c). The
initialize() method is called by the UIManager class before it gets the UIDefaults class
and is used to set varies aspects of your look and feel. On the reverse side is the
uninitialize() method. This is called the before replacing the look and feel and used to
unset varies aspects of the look and feel. The provideErrorFeedback(Component c) is
called when an illegal action is preformed by the user “such as pasting into an uneditable
JTextField that has focus” (Java API).
UIDefaults class
The UIDefaults class is a hash table of all the pieces of the look and feel.
It is this class to which the developer will add all the components of the look and feel
and from which the UIManager will retrieve components. The UIManager will get the
UIDefaults class from the LookAndFeel class of the look and feel package through the
getDefaults() method of the LookAndFeel class. The UIDefaults class is made of
key/value pairs, where the key is almost always a String and the value usually
implements the UIResource interface. If the value is a UIResource then it is almost
always a component object (Insets, Color, UI Class, etc.) and when it does not implement
UIResource then it is almost always a String. In order to set the key/value pair the
developer will need to create a single dimensional Object array that contains the keys and
values by alternating between key and the value to that key. Then, once the array is
created, it is passed to the putDefaults(Object[] objs) method of the UIDefaults object.
Java Look and Feel
7
William Pohlhaus
Example of such an array:
/* 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
key
key’s value
key
key’s value
Or
UIDefaults table
…
Object[] key_values = {
/*
Key
Value */
“desktop”,
“#FFFFFF”,
“Button.margin”,
new InsetsUIResource(1,1,1,1)
};
table.putDefaults(key_values);
Many of the key/value pairs within the UIDefaults class will only be used internally to
the look and feel package and will be created by the developer for the developer’s use.
However, it is this class that holds the matching between the UI classes and the
UIClassID’s that the lightweight Swing classes use to obtain the UI class from the
UIManager. Therefore, the developer is obligated to set all the UI classes key/value pairs
for the lightweight Swing class. The methodology behind the UIDefaults class is that this
class allows the developer to centrally control the look and feel by having the UI classes
pull default values for components from the UIDefaults through the UIManager’s static
methods. So while only the UIClassID/UI class pairs need be set, it behooves the
developer to set all the default components (insets, colors, borders, etc.) to key/value
pairs within this class.
UIResource interface
The UIResource interface is an interface that has no methods to implement. Its use is
that of a flag. It allows the developer of the look and feel package, if the developer
implements it, to distinguish between user set components (colors, icons, etc.) of a
lightweight Swing class and those components set by the look and feel. Through this
Java Look and Feel
8
William Pohlhaus
distinguishing the developer and the look and feel package can allow the user to override
the look and feel components for a particular instance of a lightweight Swing object. This
way when a look and feel is set for an application in which the Swing developer has set
different components on different objects, those changes to those particular objects will
not be overwritten by the look and feel. All of the default components within the current
look and feels implement this interface and therefore the developer is strongly
encouraged to do the same for her/his components. In order to obtain this distinguishing
the developer must call instanceof UIResource on the object in question. There are
several classes that implement this interface for convenience: IconUIResource,
BorderUIResource, etc.
Example of distinguishing UIResource:
Color background = UIManager.getColor(“Button.background”);
if(c.getBackground()==null || (c.getBackground() instanceof UIResource))
c.setBackground(background);
Java Look and Feel
9
William Pohlhaus
Basic Look and Feel
To understand the rest of customizing a look and feel, and the last type of
foundation class (UI classes), we will talk in relation to extending the
javax.swing.plaf.basic look and feel package. To extend the javax.swing.plaf.basic the
developer will first extend the BasicLookAndFeel class into his/her own LookAndFeel
class and implement the five abstract methods. This gives the developer a compliable
look and feel package that will paint all the lightweight Swing classes. This is because the
developer has, at his/her disposal, an implementation of every UI class by extending the
javax.swing.plaf.basic look and feel package. But more importantly, extension of the
javax.swing.plaf.basic package allows for three distinct methodologies of customizing the
look and feel: through color, through components and/or through CompnentUI classes
(UI classes). The developer may apply each alone or together with one or two of the other
methodologies. The BasicLookAndFeel class breaks these into three methods:
initSystemColorDefaults(UIDefaults table), initComponentDefaults(UIDefaults table)
and initClassDefaults (UIDefaults table); each is responsible for a different methodology.
Through Color
The first, and easiest, way to customize the look and feel is through color. When
extending the BasicLookAndFeel class the developer is given a method that encapsulates
this particular way of customization, the initSystemColorDefaults(UIDefaults table)
method. To do this customization the developer will associate system color keys with
UIResource color objects or String object representing color using the HTML format for
color, #RRGGBB. If setting ColorUIResource, a convenience Color class that
implements UIResource, objects as values then the developer will add the object array to
Java Look and Feel
10
William Pohlhaus
the UIDefaults table or if using String representations of color calls the
loadSystemColors(UIDefaults table, Object[] colors, boolean native) method of the
BasicLookAndFeel class. This method converts the Strings to ColorUIResource objects
and sets the system colors the developer did not set. The first two arguments of the
method are obvious, the last argument, the boolean argument, is true if the developer
wants the changes to be overwritten by the operating system’s native colors; but, most of
the time, the developer does not want this so it will be set to false. The determining factor
in using one method or the other is whether or not the developer will be setting all or
some of the system colors. If s/he is setting all the colors the former method should be
used otherwise the latter should be used. By setting various keys the developer has color
control of various groupings of components of lightweight Swing objects. With a little
trail and error, mastering this method of customization is easy. Appendix B gives a quick
reference to system colors.
Example of setting every system color:
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 */
Java Look and Feel
11
William Pohlhaus
"scrollbar"
, pr3, /* Scrollbar background (usually the "track") */
"info"
, wht, /* Background color for informational text */
"infoText"
, blk /* Color for informational text */
};
table.putDefaults(colors);
}
Example of setting only some system colors:
protected void initSystemColorDefaults(UIDefaults table) {
Object[] colors = {
"desktop",
"#CC5500",
"activeCaption",
"#FFFFFF",
"activeCaptionText", "#000000"
};
loadSystemColors(table, colors, false);
}
Through Components
It becomes evident, however, that the preceding method is limited. So this leads to
the next method of customizing look and feel, through components. To achieve this the
developer overrides the initComponentDefaults(UIDefaults table) method. Within this
method the developer associates components with particular keys that the UI classes will
use to retrieve the default values of those components. The convention for the key
naming is to name it by the type of ComponetUI class that is going to use it and the
component’s type, i.e. key “button.border” would be the border component used by the
ComponentUI button class. To truly master this methodology the developer should
become familiar with implementing all the different components, borders, icons, fonts,
insets, etc. 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. This is because all types of a
component will be centrally located in one class so the management is easier. For
example, all your borders will be inner classes of and created by your border factory
class. Just as the developer centralizes the colors, components and UI classes to the
UIDefaults table so a factory class centralizes the management of a particular type of
Java Look and Feel
12
William Pohlhaus
component. This methodology, customizing through components, is best for customizing
check boxes, radio buttons and/or other icon based components because it is the icon that
makes those lightweight Swing classes unique. An illustration of this is in the following
example (see figure 1 below to see the effects of changing the check box icon.) Note that
the every color component by default is mapped to one of the system colors, which were
set by the initSystemColorDefaults method, by the javax.swing.plaf.basic package.
Therefore, it is advisable that the developer not set color components unless s/he has truly
thought it over or wants more exacting control over a particular UI class color. See Java
Swing 2nd Edition by O’Reilly Appendix A for a complete list of all the components set
by the BasicLookAndFeel class.
Example of setting components:
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),
"RadioButton.icon" , IconFactory.getRadioButton()
};
table.putDefaults(components);
}
Note:
That super.initComponentDefaults(table) was called here but not in the
initSystemColorDefaults method.
Figure 1: illustrating how changing an icon can completely change the look and feel of
the JCheckBox
Notes about images:
Where images should be located
Java Look and Feel
13
William Pohlhaus
All the images that the look and feel uses should be located within the package directory
structure. The developer 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.
Example of a relative_path:
“/william/swing/plaf/blue/checked.gif”
where the package is:
william.swing.plaf.blue.*
How to load images
Use the javax.swing.ImageIcon class for retrieving Images will guaranteed the image will
be loaded in the Image object when getImage() method is called on this object. This is
easier and safer than use the DefaultToolkit from the java.awt.Toolkit class for loading
images
Through UI Classes
Though the preceding methodology of customization gives even greater control it
is still limited. For example, the developer cannot achieve rounded buttons through
changing components. Hence, the last methodology of customization of look and feel
gives the developer full control of the painting of the lightweight Swing object to the
developer, through UI classes. To achieve this the developer must override the
initClassDefaults(UIDefaults table) and set the key/value pairs of the UI classes, which
the developer has developed, to lightweight Swing class the developer wishes to change.
The keys will be the String returned by the lightweight Swing class’s getUIClassID()
method and the values will be the a String that holds the fully qualified name, the
package name plus the class name , of the UI class. The convention for naming the UI
class is name of the look and feel + UIClassID of the lightweight Swing class. This
methodology is best for those lightweight Swing classes that can not be customized to the
developer’s needs through one of the first two methodologies.
Java Look and Feel
14
William Pohlhaus
Example of setting UI classes:
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);
}
UI Class
The UI class, or ComponentUI class, is the class that is ultimately responsible for
painting how the lightweight Swing objects is going to look to the graphical device.
Fortunately for the developer the javax.swing.plaf.basic package has implemented each
of the ComponentUI classes for each lightweight Swing class; this leaves the developer
free to focus on painting the lightweight Swing object. In order to create a ComponentUI
class the developer needs only to extend the ComponentUI class s/he wants to create
from the javax.swing.plaf.basic package and override one method, the
createUI(JComponent c) method. In overriding this method the developer has two
choices of the type of object to return. The developer can return a singleton or can return
a new instance of the object. The developer should be familiar with returning a new
instance. To return a singleton1 the developer returns a static class variable of the
ComponentUI. The advantage of this is that it will take up less memory, but the trade-off
is every lightweight Swing class that uses this singleton will share the same state
information. However, this can be overcome by using the lightweight Swing object to
hold the state information for look and feel. Returning a singleton is the preferred
method. Only when the lightweight Swing object can not hold absolutely critical state
1
An object in which there is only one instance of it in memory
Java Look and Feel
15
William Pohlhaus
information and the developer knows there will be a low number of instances of this
object is returning a new instance acceptable.
Examples of singleton and new instance:
//From william.swing.plaf.blue.BlueButtonUI
//which is responsible 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();
}
The developer has now created a ComponentUI class but has not customized it. In order
to customize the ComponentUI the developer needs to be aware of a few other methods.
These are installUI(JComponent c), uninstallUI(JComponent c), the sizing methods
(getMinimumSize(), getMaximumSize(), getPreferredSize()), and most importantly
paint(Graphics g, JComponent c).
Methods
The installUI(JComponent c) method is called by the lightweight Swing object
when it sets the ComponentUI and passes itself to the method.
It is used to set the JComponent’s look and feel state information through its look and
feel set methods, i.e. setBackground(Color bg), if the user has not set those properties.
This can be checked by retrieving the information from its look and feel get methods, i.e.
getBackground(), and doing instanceof UIResource if true then the user has not set the
property. If the user has not set a particular property then the developer should set it by
retrieving the default value from one of the appropriate static methods from UIManager,
i.e. getColor(Object key). (See code for checking UIResourse) The other two functions to
Java Look and Feel
16
William Pohlhaus
be preformed by this method are to install listeners on the JComponent for setting its
function state information and registering keyboard actions. More often then not if the
developer is extending the javax.swing.plaf.basic package then this method won’t be
overwritten, and even if the developer does overwrite this method the
javax.swing.plaf.basic class s/he is extending will most likely have an
installListeners(JComponent c) method. On the reverse side, the uninstall(JComponent c)
is called before a new ComponentUI class is set on the old ComponentUI class and is
used to remove any listeners, keyboard actions and defaults. The developer probably
won’t override this method. The sizing methods, getMinimumSize(), getMaximumSize()
and getPreferredSize() are used by varies layout managers for finding sizing of the
JComponent, however some layout managers ignore these, i.e. GridLayout and
BorderLayout. Finally, the most important method for customizing a look and feel for a
JComponent, is the paint(Graphics g, JComponent c). The developer is expected to obtain
most of state information, both functional (is the button pressed) and graphical (what type
of borders to use), from the JComponent c object that is passed in and paint it to the
Graphics g object. Appendix C shows the william.swing.plaf.blue.BlueButtonUI class
which shows these methods and shows the use of some other useful classes.
Java Look and Feel
17
William Pohlhaus
Example of a simple RootPanelUI class that will always have a dark blue background:
package william.swing.plaf.blue;
import
import
import
import
import
import
java.awt.Color;
java.awt.Graphics;
javax.swing.JComponent;
javax.swing.JRootPane;
javax.swing.plaf.ComponentUI;
javax.swing.plaf.basic.BasicRootPaneUI;
public class BlueRootPaneUI extends BasicRootPaneUI {
public BlueRootPaneUI() {
super();
}
public void paint(Graphics g, JComponent c) {
JRootPane p = (JRootPane)c;
//Note the user can not change the background color of the JRootPanel
//developers should stay away from the practice of force components on the
//user
g.setColor(new Color(0,0,127));
}
}
public static ComponentUI createUI(JComponent c) {
return new BlueRootPaneUI();
}
Note on transparence:
The effects of setting the alpha bit in Java for the Color object can result in
unpredicted effects so the developer is advised to use it will caution.
Conclusion
That is how Java look and feel can be created and with a some practice and
exploration of different lightweight Swing classes the developer can truly call himself or
herself a look and feel developer. However, there is still future research that needs to be
done on developing a look and feel for Java Swing. Those topics are GTK for Java 1.4,
lazy and active values and how motion is achieved. Java 1.4 allows for the creation of
look and feel through the GTK+, a GNU project for creating a toolkit for creating user
interfaces (http://www.gtk.org/). Lazy and active values are types of UIResources that are
loaded differently depending if it is lazy or active. The final topic for future research is
how motion is achieved in Java look and feel, how are frames painted for minimization or
how are menus scrolled? Even so, with these methodologies the look and feel discussed
here the developer can completely create the look and feel s/he wants.
Java Look and Feel
18
William Pohlhaus
Appendix A: UML Sequence Diagram
UML Sequence Diagram showing how a JComponent or lightweight Swing object sets a
ComponentUI or UI object:
Marc Loy, Robert Eckstein, Dave Wood, James Elliott and Brain Cole, Java Swing, 2nd,
(Sebastopol: O’Reilly, 2003), 1012.
Java Look and Feel
19
William Pohlhaus
Appendix B: System Color Reference
desktop
activeCaption
activeCaptionText
activeCaptionBorder
inactiveCaption
inactiveCaptionText
inactiveCaptionBorder
window
windowBorder
windowText
menu
menuText
text
textText
textHighlight
textHighlightText
textInactiveText
control
controlText
controlHighlight
controlLtHighlight
controlShadow
controlDkShadow
scrollbar
info
infoText
Color of the desktop background
Color for captions (title bars) when they are active
Text color for text in captions (title bars)
Border color for caption (title bar) window borders
Color for captions (title bars) when not active
Text color for text in inactive captions (title bars)
Border color for inactive caption (title bar) window borders
Default color for the interior of windows
Color of the window border
Color of the window text
Background color for menus
Text color for menus
Text background color
Text foreground color
Text background color when selected
Text color when selected
Text color when disabled
Default color for controls (buttons, sliders, etc)
Default color for text in controls (buttons, sliders, etc)
Highlight color for controls
Lighter highlight color for controls
Shadow color for controls
Dark shadow color for controls
Scrollbar background (usually the "track")
Background color for informational text
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.
Java Look and Feel
20
William Pohlhaus
Appendix C: BlueButtonUI class
The william.swing.plaf.blue.BlueButtonUI class
package william.swing.plaf.blue;
import
import
import
import
import
import
import
java.awt.*;
java.awt.image.*;
javax.swing.*;
java.awt.geom.*;
javax.swing.plaf.*;
javax.swing.plaf.basic.*;
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;
}
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*/
}
//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));
Java Look and Feel
21
William Pohlhaus
//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());
//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;
}
//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);
Java Look and Feel
22
William Pohlhaus
}
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());
}
Java Look and Feel
23
William Pohlhaus
Appendix D: Resources
5 really good Resources
1. 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 and used
 ISBN 0-596-00408-7
2. The Java API Documentation
 Always a good resource
 http://java.sun.com/apis.html#j2se
3. The Java base API source code
 See what the guys at Sun did
 http://java.sun.com/j2se/downloads.html
4. 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
5. Java Developer’s forums
 Get help from other developers
 http://forum.java.sun.com/
Java Look and Feel
24