Professional Documents
Culture Documents
Introduction
This presentation is designed to show you how to create a Java Look and Feel for the javax.swing package.
Image classes
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.
Resources to Consider
A good book on Java Swing preferably one with a chapter or two on look and feel
ISBN 0-596-00408-7
Always a good resource http://java.sun.com/apis.html#j2se See what the guys at Sun did http://java.sun.com/j2se/downloads.html See what someone else did, the guys at Sun tend to be more complicated then need be Get help from other developers http://forum.java.sun.com/
Why Bother
Games
Current Look and Feels were not designed with games in mind Company/Software Logo, Trademark, Distinct Look and Feel
Marketability
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.
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.
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 UIManagers 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 UIManagers getUI method to fetch the corresponding UI class The UIManager, which holds a copy of the UIDefaults class from LookAndFeel class, calls UIDefaults classs 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 classs static createUI method is called which returns an instance of the UI class The Lightweight Java Swing class then uses the UI classs paint method when it is asked to paint or repaint itself
Marc Loy, Robert Eckstein, Dave Wood, James Elliott and Brain Cole, Java Swing, 2nd, (Sebastopol: OReilly, 2003), 1012.
Getting Started
Extending your Look and Feel from the javax.swing.plaf.basic package or other look and feel packages
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
This is the class that is passed into UIManager.setLookAndFeel method This class is the center of your look and feel
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
getDescription()
Returns a String describing this look and feel Returns a String with the ID of this look and feel Returns a String with the name of this look and feel 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 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
getID()
getName()
isNativeLookAndFeel()
isSupportedLookAndFeel()
Static methods
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 Invoked when the user performs an illegal action such as pasting into an uneditable JTextField that has focus Java API
uninitialize()
provideErrorFeedback(Component c)
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
Three methods that give you complete control of your look and feel
initSystemColorDefaults(UIDefaults table)
Sets the colors of your look and feel Sets the icons, borders, etc. of your look and feel Sets the UI Classes corresponding to the JComponents they are responsible for painting
initComponentDefaults(UIDefaults table)
initClassDefaults(UIDefaults table)
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
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 feels 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 wont 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
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
Add your key/value pairs to the Object[] by setting the first key to the Object[] at index 0 followed by its 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.
// // // //
System Colors
Every color used by the BasicLookAndFeels 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 Two primary ways
One
Setting every System Colors key/value pair with ColorUIResource Objects Passing the Object[] holding them in the UIDefaults table 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
Two
System colors
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: OReilly, 2003), 1059. and Java Source Code for javax.swing.plaf.basic.BasicLookAndFeel Sun Mircosystems Inc.
Setting Components
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 2nd Edition by OReilly 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
ComponentUI Classes are the classes responsible for painting the varies JComponents The keys to the ComponentUI class must match what is returned by the JComponents 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 StringButtonUI
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
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
ComponentUI Classes
These are the classes responsible for painting the JComponents to the graphical device
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
How
By returning a static class variable of the ComponentUI class Uses less memory because there is only one instance of the ComponentUI class that is responsible for painting 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
Pros
Cons
How
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 Uses more memory because each JComponent that needs this ComponentUI class will have an instance of it
Pros
Cons
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
Or //As a new instance public static ComponentUI createUI(JComponent c) { return new BlueButtonUI(); }
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 Listeners are responsible for setting the state of the JComponent through the varies setters and getters in the JComponent
Install listeners
i.e. on a mouse Pressed event of a JButton the listener will set the JButtons pressed class variable to true through setPressed(boolean p) method
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 wont 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
More often then not this method wont 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 its UI class
Sizing methods
Used by various Layout Managers to get the size of the Graphics object for the JComponent
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
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.
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
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
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
//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; }
It should be noted that this would be better as a radio button then a check box due to its round nature
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
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
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; }
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 elses 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
Component
Any object or class that is use to help create the look and feel Parent class of all UI classes; any UI class (see UI Class) A class whose sole purpose it to return instances of classes through factory methods A static method that is responsible for returning and instance of a class, whether it be a singleton or new instance Top level swing classes; painting is partly or completely controlled by the OS Parent class of Lightweight Java Swing classes; any Lightweight Java Swing class (see Lightweight Java Swing Class) A Swing class that allows for pluggable look and feel through UIManager.setLookAndFeel An instance of a class in which there will be only one instance within of it within the JVM A class that is responsible for paint a JComponent class
ComponentUI
Factory Class
Factory method
JComponent
Singleton
UI Class
Author
By William Pohlhaus
Email:
william.pohlhaus@villanova.edu http://www.homepage.villanova.edu/william.pohlhaus
Homepage: