580 likes | 984 Views
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
E N D
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 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);
Setting System Colors • Override the initSystemColorDefaults(UIDefaults table) • System Colors • Every color used by the BasicLookAndFeel’s UI classes is set by default to one of the System Colors • This givens one global control of all the colors in the look and feel • Overriding the method • Two primary ways • One • Setting every System Colors’ key/value pair with ColorUIResource Objects • Passing the Object[] holding them in the UIDefaults table • Two • Setting some or all the System Colors’ key/value pairs using String representations for the color values • Passing the UIDefaults class, the Object[] with the key/value pairs to the loadSystemColors method
System colors • These are the System colors that the BasicLookAndFeel uses • desktop Color of the desktop background • activeCaption Color for captions (title bars) when they are active • activeCaptionText Text color for text in captions (title bars) • activeCaptionBorder Border color for caption (title bar) window borders • inactiveCaption Color for captions (title bars) when not active • inactiveCaptionText Text color for text in inactive captions (title bars) • inactiveCaptionBorder Border color for inactive caption (title bar) window borders • window Default color for the interior of windows • windowBorder Color of the window border • windowText Color of the window text • menu Background color for menus • menuText Text color for menus • text Text background color • textText Text foreground color • textHighlight Text background color when selected • textHighlightText Text color when selected • textInactiveText Text color when disabled • control Default color for controls (buttons, sliders, etc) • controlText Default color for text in controls (buttons, sliders, etc) • controlHighlight Highlight color for controls • controlLtHighlight Lighter highlight color for controls • controlShadow Shadow color for controls • controlDkShadow Dark shadow color for controls • scrollbar Scrollbar background (usually the "track") • info Background color for informational text • infoText Color for informational text • Marc Loy, Robert Eckstein, Dave Wood, James Elliott and Brain Cole, Java Swing, 2nd, (Sebastopol: O’Reilly, 2003), 1059. and Java Source Code for javax.swing.plaf.basic.BasicLookAndFeel Sun Mircosystems Inc.
Example Code for Overriding initSystemColorDefaults(UIDefaults table) //By setting every System color key protected void initSystemColorDefaults(UIDefaults table) { ColorUIResource pr1 = new ColorUIResource(new Color(127,127,255)); ColorUIResource pr2 = new ColorUIResource(new Color(0,0,127)); ColorUIResource pr3 = new ColorUIResource(new Color(63,63,191)); ColorUIResource pr4 = new ColorUIResource(new Color(0,0,255)); ColorUIResource blk = new ColorUIResource(Color.BLACK); ColorUIResource wht = new ColorUIResource(Color.WHITE); ColorUIResource gry = new ColorUIResource(Color.GRAY); Object[] colors = { "desktop" , pr1, /* Color of the desktop background */ "activeCaption" , pr3, /* Color for captions (title bars) when they are active. */ "activeCaptionText" , wht, /* Text color for text in captions (title bars). */ "activeCaptionBorder" , blk, /* Border color for caption (title bar) window borders. */ "inactiveCaption" , pr1, /* Color for captions (title bars) when not active. */ "inactiveCaptionText" , gry, /* Text color for text in inactive captions (title bars). */ "inactiveCaptionBorder" , gry, /* Border color for inactive caption (title bar) window borders. */ "window" , wht, /* Default color for the interior of windows */ "windowBorder" , blk, /* Color of the window border */ "windowText" , blk, /* Color of the window text */ "menu" , pr1, /* Background color for menus */ "menuText" , blk, /* Text color for menus */ "text" , pr1, /* Text background color */ "textText" , blk, /* Text foreground color */ "textHighlight" , pr4, /* Text background color when selected */ "textHighlightText" , wht, /* Text color when selected */ "textInactiveText" , gry, /* Text color when disabled */ "control" , pr1, /* Default color for controls (buttons, sliders, etc) */ "controlText" , blk, /* Default color for text in controls (buttons, sliders, etc) */ "controlHighlight" , pr4, /* Highlight color for controls */ "controlLtHighlight" , wht, /* Lighter highlight color for controls */ "controlShadow" , pr2, /* Shadow color for controls */ "controlDkShadow" , blk, /* Dark shadow color for controls */ "scrollbar" , pr3, /* Scrollbar background (usually the "track") */ "info" , wht, /* Background color for informational text */ "infoText" , blk /* Color for informational text */ }; table.putDefaults(colors); }
Example Code for Overriding initSystemColorDefaults(UIDefaults table) /*By setting only sum and using String representations of the colors */ protected void initSystemColorDefaults(UIDefaults table) { Object[] colors = { "desktop“, “#CC5500”, "activeCaption“, “#FFFFFF”, "activeCaptionText“, “#000000” }; loadSystemColors(table, colors, false); /* the last value false is saying not to override your changes with the native system colors */ }
Setting Components • Override the initComponentDefaults(UIDefaults table) • Components are various objects used by the UI classes • Borders, Insets, Fonts, Colors, etc • By 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 voidpaint(Graphics g, JComponent c) • Thismethod 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.IconUIResource public class CheckBoxIcon implements UIResource, Icon, Serializable { //two images to represent the checkbox being unchecked and checked Image unchecked = null, checked = null; //the size of this icon int width = 20, height = 20; //the constructor is loading the images public CheckBoxIcon() { unchecked = new ImageIcon(this.getClass().getResource("/william/swing/plaf/blue/unchecked.gif")).getImage(); checked = new ImageIcon(this.getClass().getResource("/william/swing/plaf/blue/checked.gif")).getImage(); } public int getIconWidth() { return width; } public int getIconHeight() { return height; }
Example of a Checkbox Icon Component cont. /*c is the checkbox, g is the Graphics object from the paint method of the CheckboxUI class x and y are where the icon is to be painted on the Graphics object g */ public void paintIcon(Component c, Graphics g, int x, int y) { /*This icon will be used with a JCheckBox so the assumption can be made to cast it as an AbstractButton will provide state information of the checkbox */ ButtonModel model = ((AbstractButton)c).getModel(); //This ant aliasing provides a nice blended into the background ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); //Drawing the unchecked image by default at x and y g.drawImage(unchecked,x,y,width,height,null); //If the checkbox is checked then paint over the unchecked image with the checked image if(model.isSelected()||model.isPressed()||model.isArmed()) g.drawImage(checked,x,y,width,height,null); //Drawing a 2px circle around the checkbox g.drawOval(x+1,y+1,width-1,height-1); g.drawOval(x,y,width,height); } }
Example of a Checkbox Icon Component cont. //Then you just add it to the UIDefaults table protected void initComponentDefaults(UIDefaults table) { super.initComponentDefaults(table); Object[] components = { "CheckBox.icon", new CheckBoxIcon() }; table.putDefaults(components); } The checkbox goes from this: To this: It should be noted that this would be better as a radio button then a check box due to its round nature
Example of a Checkbox Icon Component cont. • Two notes about using images • One • One can obtain an URL object representing the locate of a resource relative to your class path by invoking this.getClass().getResource(String relative_path) method • This way one can store all images within the look and feel package for that look and feel • Two • By using the javax.swing.ImageIcon class for retrieving Images one is guaranteed the image will be loaded in the Image object when getImage() method is called • This is easier and safer than use the DefaultToolkit from the java.awt.Toolkit class for loading images
Through ComponentUI Classes • This is the hardest but gives the greatest control • Override the initClassDefaults (UIDefaults table) method • The example is of ComponentUI responsible for painting JButton • It is good to understand this because of the 40 or so JComponents 8 are buttons and behave very similarly to JButton • This example shows how one has to do the following • Painting the button • Painting the text if there is text • Painting the icon if there is an icon • Setting up the listeners • Determining user values look and feel set values • A singleton
Example of a Button ComponentUI package william.swing.plaf.blue; import java.awt.*; import java.awt.image.*; import javax.swing.*; import java.awt.geom.*; import javax.swing.plaf.*; import javax.swing.plaf.basic.*; import javax.swing.text.View; public class BlueButtonUI extends BasicButtonUI { //The singleton istance of BlueButtonUI static BlueButtonUI b = new BlueButtonUI(); //Default background and foreground Color background; Color foreground; //There will be only one font for this these buttons Font font; public BlueButtonUI() { super(); } //The factory method returns the singleton public static ComponentUI createUI(JComponent c) { return b; }
Example of a Button ComponentUI cont. public void installUI(JComponent c) { //Since we know this is a JButton it is safe to cast as an AbstractButton AbstractButton b = (AbstractButton)c; //Setting the default values from the UIDefaults table background = UIManager.getColor("Button.background"); foreground = UIManager.getColor("Button.foreground"); font = UIManager.getFont("Button.font"); //Checking for user set values for foreground and background before setting them //Note that the font compnonent is not checked therefore the value from the UIDefaults table will //override the user’s values (This is not recommended) further not all the defaults are set if(c.getBackground()==null || (c.getBackground() instanceof UIResource)) c.setBackground(background); if(c.getForeground()==null || (c.getForeground() instanceof UIResource)) c.setForeground(foreground); //Using BasicButtonUI installListeners method to install listeners super.installListeners(b); /*Note that there are no keyboard registations, therefore hit any of the keys will not invoke an event*/ }
Example of a Button ComponentUI cont. //Paints a rounded button that is semi-transparent with lines public void paint(Graphics g, JComponent c) { //Once again it is safe to cast as an AbstractButton because we know it is a JButton AbstractButton b = (AbstractButton)c; //The ButtonModel holds a lot of the functional state of the button ButtonModel model = b.getModel(); //Casting to a Graphics2D for convenience, this is safew because we know that the g object is really a Graphics2D object Graphics2D g2 = (Graphics2D)g; //Sets the arcs widths and heights int arc_w = (int)c.getHeight()/2; int arc_h = arc_w; Insets i = c.getInsets(); //Gets the area for the text and icon to be painted in with respects to the insets Rectangle viewRect = new Rectangle(i.left,i.top,b.getWidth()-(i.right+i.left),b.getHeight() - (i.bottom + i.top)); //the area that the text will be drawn in that will be defined //by SwingUtilities.layoutCompoundLabel Rectangle textRect = new Rectangle(0,0,0,0); //the area that the icon will be drawn in that will be defined //by SwingUtilities.layoutCompoundLabel Rectangle iconRect = new Rectangle(0,0,0,0); //I have opted to set the base font size on the size of the button this will cause the font size to skrink or grow with respect to the button size int fontSize = (int)c.getHeight()/3; if(fontSize<8) fontSize = 8; g2.setFont(new Font(font.getName(),font.getStyle(),fontSize)); //modify text for display, will add ... if clipped and //determine the text area and icon area String text = SwingUtilities.layoutCompoundLabel( c, g2.getFontMetrics(), b.getText(), b.getIcon(), b.getVerticalAlignment(), b.getHorizontalAlignment(), b.getVerticalTextPosition(), b.getHorizontalTextPosition(), viewRect, iconRect, textRect, b.getText() == null ? 0 : b.getIconTextGap());
Example of a Button ComponentUI cont. //Starting with a BufferedImage because the graphics object from a BufferedImage respects composite overlay directives //NOTE the Graphics object passed in to this method does not respect these directives BufferedImage buffImg = new BufferedImage(c.getWidth(), c.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D gbi = buffImg.createGraphics(); //Retrieving the state of the colors from the component which were set in the installUI method Color back = c.getBackground(); Color fore = c.getForeground(); //creating a semi-transparent background for the button Color bg = new Color(back.getRed(),back.getGreen(),back.getBlue(),127); //Defining the color of my borders Color wh = Color.WHITE; Color gr = Color.GRAY; //if button is pressed change the background to dark and switch the border colors (this makes it appear that the button is pressed in) if (model.isArmed() && model.isPressed()) { Color d = back.darker().darker().darker(); bg = new Color(d.getRed(),d.getGreen(),d.getBlue(),127); wh = Color.GRAY; gr = Color.WHITE; } //set background color gbi.setColor(bg); gbi.fillRoundRect(0,0,c.getWidth(),c.getHeight(),arc_w,arc_h); //lay in the strips gbi.setColor(Color.BLACK); gbi.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN,1.0f)); gbi.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); for(int j=0;j<c.getHeight();) { gbi.fillRect(0,j,c.getWidth(),2); j=j+4; }