Blue look and feel
-
Upload
hariprasanna-v -
Category
Documents
-
view
596 -
download
2
description
Transcript of Blue look and feel
Java Swing Look and Feel
Creation of a Look and Feel package
Introduction
This presentation is designed to show you how to create a Java Look and Feel for the javax.swing package.
Note Before Getting Started
You should have a basic understanding of the Java 2D API java.awt.Graphics and java.awt.Graphics2D classes Shape classes
java.awt.Rectangle, java.awt.image.Arc, etc. Image classes
java.awt.Image, java.awt.images.BufferedImage, etc. java.awt.Font and java.awt.Color
You should have a basic understanding of the Java Swing API The various JComponents (JButton, JPanel, etc)
You are given great freedom in creating your Java look and feel package. Therefore, you should be familiar with other Java look and feel packages so when creating your own it behaves in a similar manner to other Java look and feel packages. javax.swing.plaf.metal look and feel package
Resources to Consider
5 really good Resources A good book on Java Swing preferably one with a chapter or two on look
and feel Java Swing 2nd edition from O’Reilly is what I recommend
ISBN 0-596-00408-7 The Java API Documentation
Always a good resource http://java.sun.com/apis.html#j2se
The Java base API source code See what the guys at Sun did http://java.sun.com/j2se/downloads.html
Someone else’s look and feel source code See what someone else did, the guys at Sun tend to be more complicated
then need be Java Developer’s forums
Get help from other developers http://forum.java.sun.com/
Why Bother
Games Current Look and Feels were not designed with games in mind
Marketability Company/Software Logo, Trademark, Distinct Look and Feel
Company Standards for in house applications Quicker Development of Applications
By splitting the look and feel from functionality you can have two different teams developing simultaneously each working on different aspects (functionality and look) at the same time
Easier control of look and feel Rather then setting colors, borders, icons, etc. for each instance of
the swing components you plan on using. Your swing applications might be running on device rather
than a PC
How Java Look and Feel Works in a Nutshell
Each Java Swing lightweight class or JComponent has a corresponding user interface class (UI class or ComponentUI) that is responsible for painting the component to the graphical device. These are controlled 100% by the user Java Swing components that do not have a corresponding UI class are the four heavyweight
components: JFrame, JDialog, JWindow, and JApplet. These are controlled by the Operating System and user.
Depending on the OS the user has different levels of control The LookAndFeel class holds meta information about the look and feel
Within the LookAndFeel class the UIDefaults class associates the UI classes, which are created by the look and feel developer, with the Lightweight Java Swing classes they are suppose to paint
The UIManager’s setLookAndFeel methods are used to set which LookAndFeel class is used to associate UI classes with Lightweight Java Swing classes
Once the LookAndFeel class is set any call to paint a Lightweight Java Swing class to the graphical device will use the associated UI class The Lightweight Java Swing class calls the UIManager’s getUI method to fetch the
corresponding UI class The UIManager, which holds a copy of the UIDefaults class from LookAndFeel class, calls
UIDefaults class’s getUI method UIDefaults obtains the UIClassID from the calling Lightweight Java Swing class then it gets the
corresponding UI class through reflection Once the UI class is obtained the UI class’s static createUI method is called which returns an
instance of the UI class The Lightweight Java Swing class then uses the UI class’s paint method when it is asked to
paint or repaint itself See next slide fore detail UML Sequence Diagram
Detailed Sequence Diagram of how it works
Marc Loy, Robert Eckstein, Dave Wood, James Elliott and Brain Cole, Java Swing, 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()
Returns a String describing this look and feel getID()
Returns a String with the ID of this look and feel getName()
Returns a String with the name of this look and feel isNativeLookAndFeel()
Returns a boolean for whether or not this look and feel is native to the operating system it is instantiated on
The Windows look and feel for Java will return true on Window systems and false on all others
Custom look and feels will all always return false isSupportedLookAndFeel()
Returns a boolean for whether or not this look and feel is supported on the operating system it is instantiated on
Cross platform look and feels will usually always return true These methods are used by the UIManager class is a variety of
ways
LookAndFeel Class Cont.
Static methods The static methods are provided for convenience
Other methods to consider overriding initialize()
Used for initializing your look and feel Invoked by UIManager before getting the UIDefaults class from the
LookAndFeel class uninitialize()
Used to uninitialize your look and feel Invoked by UIManager before look and feel is replaced by another
look and feel provideErrorFeedback(Component c)
Invoked when the user performs an illegal action “such as pasting into an uneditable JTextField that has focus” – Java API
BasicLookAndFeel Class
This is the class to extend when creating your own look and feel By extending the BasicLookAndFeel Class you
get an implementation of every UIClass in the javax.swing.plaf.basic package
Aside from overriding the abstract methods from the LookAndFeel class there are only three methods you need to override to get started
BasicLookAndFeel Class cont.
Three methods that give you complete control of your look and feel initSystemColorDefaults(UIDefaults table)
Sets the colors of your look and feel initComponentDefaults(UIDefaults table)
Sets the icons, borders, etc. of your look and feel initClassDefaults(UIDefaults table)
Sets the UI Classes corresponding to the JComponents they are responsible for painting
By overriding these three methods you can customize your look and feel completely
They are called before returning the UIDefaults class to the UIManager class in the getDefaults() method of the BasicLookAndFeel class
UIDefaults class
UIDefaults This class holds all the information for your look
and feel The information is stored in key/value pairs The UIManager class retrieves Object values
from the UIDefaults class based on the key pass to one of the many static getXXX(key) methods
Most importantly it holds the Colors, Icons, Borders, Insets, and ComponentUI classes for your look and feel
Note about UIDefaults class
A majority of the key/value pairs set by the BasicLookAndFeel in the UIDefaults are only used internally in the look and feel package by the ComponentUI classes This is a good practice to continue because it gives you global control of
your look and feel through one class i.e. If one wants to change the color of the buttons in the look and feel. Then
one can change the value in the UIDefaults class without having to look for it in the button ComponentUI class
The key/value pairs that are used out of the look and feel package are the ones that match JComponent classes to ComponentUI classes
With this in mind two conclusions are drawn One is obligated to set all the key/value pairs for the ComponentUI
classes This is one reason why extending BasicLookAndFeel is a good idea because
these are all set by BasicLookAndFeel Another is that all the Icons, Borders, Colors, etc. are controlled global through
the UIDefaults class by default in BasicLookAndFeel One can add any type of key/value pair to the UIDefaults class to use
UIResource interface
This interface is responsible for separating user set colors, borders, insets, etc. from the look and feel’s colors, borders, insets, etc
It has NO methods, it is used to mark objects used by the look and feel from those objects set by the user
This allows the look and feel developer to separate those values set by the user on the JComponent object from those set by the look and feel. This way the look and feel developer won’t override those values when
setting the look and feel or switching to a different look and feel or theme (see javax.swing.plaf.metal look and feel)
Every Icon, Inset, Border, ComponentUI class, etc. for the look and feel will have to or already has implemented this interface
There are also several convenience classes that implement this interface that are useful ColorUIResource, BorderUIResource, InsetUIResource, etc.
Adding key/value pairs to the UIDefaults class
To add key/values the UIDefaults to first create an Object[] that is the twice the length of the amount of key/value pairs you want to add If you are adding 12 key/value pairs the length of the
Object[] is 24 Add your key/value pairs to the Object[] by setting
the first key to the Object[] at index 0 followed by it’s value at index 1 continue with the next key/value pair at index 2 and 3, continue until the Object[] is full
Then call the UIDefaults method putDefaults(Object[] obj) and pass the Object[] to it.
Code Example of adding Key/Value pairs to UIDefaults
/* Adding two key values to table */UIDefaults table …Object[] key_values = new Object[4]; key_values[0] = “desktop”; // 1st keykey_values[1] = “#FFFFFF”; // 1st key’s valuekey_values[2] = “Button.margin”; // 2nd keykey_values[3] = new InsetsUIResource(1,1,1,1); // 2nd key’s valuetable.putDefaults(key_values);
Or
UIDefaults table …Object[] key_values = {/* Key Value */
“desktop”, “#FFFFFF”,“Button.margin”, new InsetsUIResource(1,1,1,1)
};table.putDefaults(key_values);
Setting System Colors
Override the initSystemColorDefaults(UIDefaults table) System Colors
Every color used by the BasicLookAndFeel’s UI classes is set by default to one of the System Colors
This givens one global control of all the colors in the look and feel
Overriding the method Two primary ways
One Setting every System Colors’ key/value pair with
ColorUIResource Objects Passing the Object[] holding them in the UIDefaults table
Two Setting some or all the System Colors’ key/value pairs
using String representations for the color values Passing the UIDefaults class, the Object[] with the
key/value pairs to the loadSystemColors method
System colors
These are the System colors that the BasicLookAndFeel uses desktop Color of the desktop background activeCaption Color for captions (title bars) when they are active activeCaptionText Text color for text in captions (title bars) activeCaptionBorder Border color for caption (title bar) window borders inactiveCaption Color for captions (title bars) when not active inactiveCaptionText Text color for text in inactive captions (title bars) inactiveCaptionBorder Border color for inactive caption (title bar) window borders window Default color for the interior of windows windowBorder Color of the window border windowText Color of the window text menu Background color for menus menuText Text color for menus text Text background color textText Text foreground color textHighlight Text background color when selected textHighlightText Text color when selected textInactiveText Text color when disabled control Default color for controls (buttons, sliders, etc) controlText Default color for text in controls (buttons, sliders, etc) controlHighlight Highlight color for controls controlLtHighlight Lighter highlight color for controls controlShadow Shadow color for controls controlDkShadowDark 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 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 O’Reilly Appendix A for a complete list of
all the components set by the BasicLookAndFeel Overriding the method
Call the super.initComponentDefaults(UIDefaults table) method (unless you map all the components) This will map all the component key/value pairs from the BasicLookAndFeel Note this is not done in initSystemColorDefaults(UIDefaults table);
Set the key/value pairs that are to be changed Pass the Object[] holding the key/value pairs to the
UIDefaults.putDefaults(Object[] obj) method
Example Code for Overriding initComponentDefaults(UIDefaults table)
protected void initComponentDefaults(UIDefaults table) { super.initComponentDefaults(table); Object[] components = { "CheckBox.icon", new CheckBoxIcon(), "Button.background",pr4, "Button.foreground",wht, "Button.font", new Font("Times",Font.BOLD|Font.ITALIC,10) }; table.putDefaults(components); }
Setting ComponentUI Classes
Override the initClassDefaults(UIDefaults table) ComponentUI Classes are the classes responsible for painting the varies
JComponents The keys to the ComponentUI class must match what is returned by the
JComponent’s getUIClassID() method for the class they are to paint The convention is: the name of the JComponent minus the processing J and adding UI at
the end i.e. JButton.getUIClassID() returns the String “ButtonUI” and therefore the key for the UI class must be
the String“ButtonUI” The values must be a String object holding the complete name of the
ComponentUI class (this is the package+class name) i.e. “william.swing.plaf.blue.BlueButtonUI”
The package is william.swing.plaf.blue The class is BlueButtonUI
Overriding the method Call the super.initClassDefaults(UIDefaults table) method (unless you map all the
ComponentUI classes) This will map all the UI classes key/value pairs from the BasicLookAndFeel Note this is not done in initSystemColorDefaults(UIDefaults table);
Set the key/value pairs that are to be changed Pass the Object[] holding the key/value pairs to the UIDefaults.putDefaults(Object[] obj)
method
Example Code for Overriding initClassDefaults(UIDefaults table)
protected void initClassDefaults(UIDefaults table) { super.initClassDefaults(table); //package that the ComponentUI classes belong too String pkg = "william.swing.plaf.blue."; Object[] classes = { "RootPaneUI", pkg + "BlueRootPaneUI", "PanelUI" , pkg + "BluePanelUI", "MenuBarUI" , pkg + "BlueMenuBarUI", "MenuUI" , pkg + "BlueMenuUI", "ButtonUI" , pkg + "BlueButtonUI" }; table.putDefaults(classes); }
ComponentUI Classes
These are the classes responsible for painting the JComponents to the graphical device Each ComponentUI class is responsible for painting one
of the JComponents They all extend ComponentUI class The convention for naming the ComponentUI
Classes is the short name of the look and feel + UIClassID of the JComponent the ComponentUI is to paint i.e. BlueButtonUI (Blue + ButtonUI) is responsible for for
painting JButton which returns ButtonUI as its UIClassID
ComponentUI methods
public static ComponentUI createUI(JComponent c) The factory method that creates the UI class Two options
One: create a singleton How
By returning a static class variable of the ComponentUI class Pros
Uses less memory because there is only one instance of the ComponentUI class that is responsible for painting
Cons No stateful information can be held for a particle JComponent but rather all the JComponets that
ComponentUI class is painting for will share the same state Two: create a new instance
How By returning a new instance
Pros Stateful information may be held by the ComponentUI class of a particle JComponent and can
share state through static class variables Cons
Uses more memory because each JComponent that needs this ComponentUI class will have an instance of it
How to decide More often then not you will use a singleton because any stateful information can be stored in the
JComponent itself When the stateful information can not be stored in the JComponent and is critical to the look and feel for
that JComponent If extending the Basic Look And Feel this must be overridden and return an
instance, singleton or new, of the ComponentUI this method is a member of If not, then the Basic Look And Feel instance will be returned
Example of createUI method
//From william.swing.plaf.blue.BlueButtonUI //which is responible for painting JButtons
//As a singleton. //Note the static class variable private static BlueButtonUI blueButtonUI = new BlueButtonUI(); … public static ComponentUI createUI(JComponent c) { return blueButtonUI; }
Or
//As a new instance public static ComponentUI createUI(JComponent c) { return new BlueButtonUI(); }
ComponentUI methods cont.
public void installUI(JComponent c) Install default components (Color, Borders, etc.)
Set the components on the JComponent if the user has not set them To check if the user has set a component or not
Get it from the JComponent Test if it is an instance of UIResourse If it is not a UIResourse then the user set this value
If it is not a UIResourse the convention is to leave what the user has set
If it is a UIResourse Set the component to what the value is in the UIDefaults table Use the UIManager to get values from the UIDefaults table
Examples of methods that set component state from JMenu setForeground(Color fg) setBackground(Color bg)
The JComponent holds the various states of the components for painting itself Install listeners
Listeners are responsible for setting the state of the JComponent through the varies setters and getters in the JComponent i.e. on a mouse Pressed event of a JButton the listener will set the JButton’s pressed class variable to true
through setPressed(boolean p) method Install Keyboard Actions
Registers keyboard actions to set state of the JComponent i.e. registering the spacebar to be the same as the left mouse click
Install anything else you may need More often then not this method won’t be overridden if you are extending the Basic Look And
Feel UI class Called by JComponent when the JComponent calls setUI(ComponentUI ui) Typically the JComponent c is casted to an object of the actual class that is to be painted
i.e. if the ComponentUI class is to paint a JSeparator then cast c to a Jseparator object
Example of installUI method
//From william.swing.plaf.blue.BlueButtonUI which is responible for painting JButtons
public void installUI(JComponent c) { /*Casting the JComponent c to AbstractButton b (several Lightweight Java Swing class extend
AbstractButton)*/ AbstractButton b = (AbstractButton)c; /*Setting the default components for the JButton Getting the default font, background color and foreground color from the UIDefaults table
for buttons*/ background = UIManager.getColor("Button.background"); foreground = UIManager.getColor("Button.foreground"); font = UIManager.getFont("Button.font");
/*Setting background and foreground colors on the JComponent c if the user did NOT set them These values can be retrieved later by the paint method through c*/ if(c.getBackground()==null || (c.getBackground() instanceof UIResource)) c.setBackground(background); if(c.getForeground()==null || (c.getForeground() instanceof UIResource)) c.setForeground(foreground);
//Using BasicButtonUI installListeners method to install listeners super.installListeners(b); /*Note that there are no keyboard registations, therefore hit any of the keys will not invoke
an event*/ }
ComponentUI methods cont.
public void uninstallUI(JComponent c) Used to remove and cleanup
Listeners Keyboard Actions Components Anything else
More often then not this method won’t be overridden if you are extending the Basic Look And Feel UI class
Called by JComponent when the JComponent calls setUI(ComponentUI ui) if and only if the JComponent previously had a set it’s UI class
Example of uninstallUI method
//From javax.swing.plaf.basic.BasicButtonUI
public void uninstallUI(JComponent c) { //Uninstalling keyboard registations uninstallKeyboardActions((AbstractButton)
c); //Uninstalling listeners uninstallListeners((AbstractButton) c); //Uninstalling default components uninstallDefaults((AbstractButton) c); }
ComponentUI methods cont.
Sizing methods Three sizing methods
getMinimumSize() getMaximumSize() getPreferredSize()
Used by various Layout Managers to get the size of the Graphics object for the JComponent Some will ignore these values
i.e. GridLayout and BorderLayout
ComponentUI methods cont.
public void paint(Graphics g, JComponent c) This method is responsible for painting the JComponent to
the graphical device Most of the work will be done here Graphical state and function state information is obtained
from the JComponent Examples of Graphical states
Foreground color Background color Borders
Examples of Function states Is the button pressed
The Graphics object is really a Graphics2D object
Example of paint method
//Example of painting semi-translucent lines on a MenuBar
public void paint(Graphics g, JComponent c) { super.paint(g, c);
//Get the state of the background color from the JComponent Color bg = c.getBackground().darker().darker(); g.setColor(new Color(bg.getRed(),bg.getGreen(),bg.getBlue(),127));
/*Added lines at every 5 pixels of 1 pixel height and the width of the JMenuBar
Note the getting of the width and height from the JMenuBar and that the Graphic starts at 0,0*/
for(int j=0;j<c.getHeight();) { g.fillRect(0,j,c.getWidth(),1); j=j+5; } }
Non-ComponentUI methods
If you are extending the Basic look and feel you will have various methods available to use depending on the UI Class.
3 Ways to Control Look and Feel
With the preceding covered, 3 ways of controlling look and feel should become evident by extending the javax.swing.plaf.basic look and feel package Through color Through components Through CompnentUI classes
Through Color
This is the simplest of the three methods Override the
initSystemColorDefaults(UIDefaults table) method Set the System colors as desired
With a little trial and error one can see quickly what System colors control what components
Through Components
This is a bit harder than through colors but gives greater control
Override the initComponentDefaults(UIDefaults table) method Set icons, borders, insets, etc. as desired
Creating your own components Become familiar with the component you are going to create It is easiest to create a factory class that will return instances of
your components, which will be inner classes of your factory class (This approach is best if you have multiple types of a particular component) All types of a component are centrally located in one class
so the management is easier i.e. all your borders will be inner classes of and created by
your border factory class
Example of a Checkbox Icon Component cont.package william.swing.plaf.blue;
import java.awt.*;import java.io.Serializable;import java.net.URL;import javax.swing.*;
//I could have implemented javax.swing.plaf.IconUIResourcepublic 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 Any object or class that is use to help create the look and feel
ComponentUI Parent class of all UI classes; any UI class (see UI Class)
Factory Class A class whose sole purpose it to return instances of classes through factory methods
Factory method A static method that is responsible for returning and instance of a class, whether it be a singleton
or new instance Heavyweight Java Swing Class
Top level swing classes; painting is partly or completely controlled by the OS JComponent
Parent class of Lightweight Java Swing classes; any Lightweight Java Swing class (see Lightweight Java Swing Class)
Lightweight Java Swing Class A Swing class that allows for pluggable look and feel through UIManager.setLookAndFeel
Singleton An instance of a class in which there will be only one instance within of it within the JVM
UI Class A class that is responsible for paint a JComponent class
Author
By William Pohlhaus Email:
[email protected] Homepage:
http://www.homepage.villanova.edu/william.pohlhaus