Sie sind auf Seite 1von 9

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.
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 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.
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());
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  
 
                 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.
 
Example of such an array:
/* Adding two key values to table */
UIDefaults table

Object[] key_values = new Object[4];
key_values[0] = “desktop”;                                                  // 1st key
key_values[1] = “#FFFFFF”;                                                // 1st key’s value
key_values[2] = “Button.margin”;                      // 2nd key
key_values[3] = new InsetsUIResource(1,1,1,1);// 2nd key’s value
table.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);
 
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 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);
 
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 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 */
  "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 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
 
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.
 
 
 
 
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
[1]
singleton 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 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 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.
 
Example of a simple RootPanelUI class that will always have a dark blue background:
package william.swing.plaf.blue;
 
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JRootPane;
import javax.swing.plaf.ComponentUI;
import 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.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.Appendix B: System
Color Reference
 
desktop                                 Color of the desktop background
activeCaption                                       Color for captions (title bars) when they are active
activeCaptionText                                Text color for text in captions (title bars)
activeCaptionBorder                           Border color for caption (title bar) window borders
inactiveCaption                    Color for captions (title bars) when not active
inactiveCaptionText                             Text color for text in inactive captions (title bars)
inactiveCaptionBorder        Border color for inactive caption (title bar) window borders
window                                  Default color for the interior of windows
windowBorder                                      Color of the window border
windowText                                           Color of the window text
menu                                                     Background color for menus
menuText                                              Text color for menus
text                                                          Text background color
textText                                   Text foreground color
textHighlight                                          Text background color when selected
textHighlightText                  Text color when selected
textInactiveText                     Text color when disabled
control                                                    Default color for controls (buttons, sliders, etc)
controlText                                            Default color for text in controls (buttons, sliders, etc)
controlHighlight                   Highlight color for controls
controlLtHighlight                                Lighter highlight color for controls
controlShadow                     Shadow color for controls
controlDkShadow                                Dark shadow color for controls
scrollbar                                                Scrollbar background (usually the "track")
info                                                         Background color for informational text
infoText                                  Color for informational text
 
Marc Loy, Robert Eckstein, Dave Wood, James Elliott and Brain Cole, Java Swing, 2nd, (Sebastopol: O’Reilly, 2003), 1059. and Java Source Code
for javax.swing.plaf.basic.BasicLookAndFeel Sun Mircosystems Inc.
Appendix C: BlueButtonUI class
The william.swing.plaf.blue.BlueButtonUI class
ackage william.swing.plaf.blue;

mport java.awt.*;
mport java.awt.image.*;
mport javax.swing.*;
mport java.awt.geom.*;
mport javax.swing.plaf.*;
mport javax.swing.plaf.basic.*;
mport javax.swing.text.View;

ublic 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;
ublic 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

ublic 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
espect 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
s 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);
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());

 
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
1.       Java Swing 2nd edition from O’Reilly is what I recommend and used
2.       ISBN 0-596-00408-7
2.       The Java API Documentation
1.       Always a good resource
2.       http://java.sun.com/apis.html#j2se
3.       The Java base API source code
1.       See what the guys at Sun did
2.       http://java.sun.com/j2se/downloads.html
4.       Someone else’s look and feel source code
1.       See what someone else did, the guys at Sun tend to be more complicated then need be
5.       Java Developer’s forums
1.       Get help from other developers
2.        http://forum.java.sun.com/
n which there is only one instance of it in memory

Das könnte Ihnen auch gefallen