26.6 Look-and-Feel Customization


In this section, we'll look at different ways that you can change the way components in your application appear. We'll start with the simplest approach making property changes on a per-component basis and work our way through several increasingly powerful (and complicated) strategies. In the last section, we'll show you how to build your own L&F from the ground up.

26.6.1 Modification of Component Properties

This is the most obvious way to change the look of a component, and it's certainly not new to Swing. At the very top of the Java component hierarchy, java.awt.Component defines a number of fundamental properties, including foreground, background, and font. If you want to change the way a specific component looks, you can always just change the value of these properties, or any of the others defined by the specific components you are using. As we said, this is nothing new to the Swing PLAF architecture, but we don't want to lose sight of the fact that you can still make many changes in this way.

Bear in mind that platform-specific L&F constraints on the rendering of certain components may prevent some property changes from being honored. For example, Mac buttons are always the same color.

26.6.2 Modification of the UI Defaults

Modifying component properties lets you customize individual components. But what if you want to make more global changes? What if you want to change things that aren't exposed as component properties?

This is where UIResources come into play. There are more than 300 different resources defined by the Swing L&Fs that you can tweak to change the way the components are displayed. These resources include icons, borders, colors, fonts, and more. (Appendix A shows a complete list of the properties defined by the BasicLookAndFeel, the base class for all of the Swing-provided L&Fs.) Unfortunately, not all of the Swing L&Fs adhere strictly to these resource names. In the following example, several of the resource names we've used are specific to the Metal L&F.

26.6.2.1 Making global changes with defaults

In this example, we change the defaults for a variety of resources to give you an idea of the types of things you can affect. Specifically, we change the border used by buttons, the title font and icons used by internal frames, and the width used by scrollbars.

Because we're making these changes globally, any components that use these properties are affected by what we do.

// ResourceModExample.java // import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; public class ResourceModExample {   public static void main(String[] args) {     // A custom border for all buttons     Border border = BorderFactory.createRaisedBevelBorder( );     Border tripleBorder = new CompoundBorder(new CompoundBorder(       border, border), border);     UIManager.put("Button.border", tripleBorder);     // Custom icons for internal frames     UIManager.put("InternalFrame.closeIcon", new ImageIcon("close.gif"));     UIManager.put("InternalFrame.iconizeIcon", new ImageIcon("iconify.gif"));     UIManager.put("InternalFrame.maximizeIcon", new ImageIcon("maximize.gif"));     UIManager.put("InternalFrame.altMaximizeIcon", new ImageIcon("altMax.gif"));     // A custom internal frame title font     UIManager.put("InternalFrame.titleFont", new Font("Serif", Font.ITALIC, 12));     // Make scrollbars really wide.     UIManager.put("ScrollBar.width", new Integer(30));     // Throw together some components to show what we've done. Nothing below here is     // L&F-specific.     // ***********************************     JFrame f = new JFrame( );     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     Container c = f.getContentPane( );     JDesktopPane desk = new JDesktopPane( );     c.add(desk, BorderLayout.CENTER);     JButton cut = new JButton("Cut");     JButton copy = new JButton("Copy");     JButton paste = new JButton("Paste");     JPanel p = new JPanel(new FlowLayout( ));     p.add(cut);     p.add(copy);     p.add(paste);     c.add(p, BorderLayout.SOUTH);     JInternalFrame inf = new JInternalFrame("MyFrame", true, true, true, true);     JLabel l = new JLabel(new ImageIcon("luggage.jpeg"));     JScrollPane scroll = new JScrollPane(l);     inf.setContentPane(scroll);     inf.setBounds(10, 10, 350, 280);     desk.add(inf);     inf.setVisible(true);     f.setSize(380, 360);     f.setVisible(true);   } }

There's not really a lot to explain about the example. In the first part, we set a variety of resources, specifying our own border, icons, font, and scrollbar width. After that, we just threw together a few components to demonstrate the changes we made. We didn't have to do anything specific to our individual internal frame, scrollbar, or button objects they used our new defaults automatically. This example produces the display shown in Figure 26-11. This example also illustrates the limitation brought up at the end of the previous section and shows that it applies to defaults in the same way it does to directly modifying component properties. Not every L&F honors all customizations that your code might request. Metal ignored the titleFont specification (as can be seen in the lefthand screenshot) while the Mac L&F used the requested font but enforced the Aqua Human Interface Guidelines with respect to the window control icons and scrollbar widths. Both L&Fs used the specified custom button borders.

Figure 26-11. UI resource customization in the Metal (left) and Mac (right) L&Fs
figs/swng2.2611.gif

26.6.3 Using Metal's Themes

The cross-platform Metal L&F defines a class called MetalTheme that allows you to customize the look of any application that uses Metal. This simple class encapsulates the colors and fonts used by the L&F. By creating your own theme, you can change the look of every component displayed by the Metal L&F.

Sound exciting? It gets better. What makes this feature especially nice is how easy it is to use. Here's an extension of the default theme (which uses three shades of blue for its primary colors) that replaces the familiar blue colors with shades of red:

// RedTheme.java // import javax.swing.plaf.*; import javax.swing.plaf.metal.*; public class RedTheme extends DefaultMetalTheme {   public String getName( ) { return "Mars"; }   private final ColorUIResource primary1 = new ColorUIResource(153, 102, 102);   private final ColorUIResource primary2 = new ColorUIResource(204, 153, 153);   private final ColorUIResource primary3 = new ColorUIResource(255, 204, 204);   protected ColorUIResource getPrimary1( ) { return primary1; }   protected ColorUIResource getPrimary2( ) { return primary2; }   protected ColorUIResource getPrimary3( ) { return primary3; } } 

These last three methods are defined by MetalTheme to set the primary colors used by the Metal L&F. Everything Metal paints uses these colors (along with three shades of gray, plus black and white), so there's nothing more to do. To tell an application to use this new theme, we just need to add the following call to the beginning of the main( ) or init( ) method:

MetalLookAndFeel.setCurrentTheme(new RedTheme( ));

If you've already created any Swing components, you'll also need to call UIManager.setLookAndFeel(new MetalLookAndFeel( )) after you set the theme. Themes are queried only during the initialization phase, when all of the data from the theme is loaded into the UIDefaults. Of course, you'll also need to make sure your application is using the Metal L&F if that's not the platform default.

26.6.3.1 MetalTheme properties

The RedTheme.java example shows the most basic type of theme you can create; it doesn't scratch the surface of the flexibility Metal themes can provide. The MetalTheme abstract class actually defines all the properties shown in Table 26-7 (note that some of the accessors are abstract in MetalTheme). The default values for all of the colors are defined by the various base colors set by the theme, along with black and white. In some cases, the defaults are defined in terms of other properties from this table.

Table 26-7. MetalTheme properties

Property

Data type

get

is

set

Default value

acceleratorForeground

ColorUIResource

·

   

primary1

acceleratorSelectedForeground

ColorUIResource

·

   

black

control

ColorUIResource

·

   

secondary3

controlDarkShadow

ColorUIResource

·

   

secondary1

controlDisabled

ColorUIResource

·

   

secondary2

controlHighlight

ColorUIResource

·

   

white

controlInfo

ColorUIResource

·

   

black

controlShadow

ColorUIResource

·

   

secondary2

controlTextColor

ColorUIResource

·

   

controlInfo

controlTextFont

FontUIResource

·

   

Abstract

desktopColor

ColorUIResource

·

   

primary2

focusColor

ColorUIResource

·

   

primary2

highlightedTextColor

ColorUIResource

·

   

controlTextColor

inactiveControlTextColor

ColorUIResource

·

   

controlDisabled

inactiveSystemTextColor

ColorUIResource

·

   

secondary2

menuBackground

ColorUIResource

·

   

secondary3

menuDisabledForeground

ColorUIResource

·

   

secondary3

menuForeground

ColorUIResource

·

   

black

menuSelectedBackground

ColorUIResource

·

   

primary2

menuSelectedForeground

ColorUIResource

·

   

black

menuTextFont

FontUIResource

·

   

Abstract

name

String

·

   

Abstract

primaryControl

ColorUIResource

·

   

primary3

primaryControlDarkShadow

ColorUIResource

·

   

primary1

primaryControlHighlight

ColorUIResource

·

   

white

primaryControlInfo

ColorUIResource

·

   

black

primaryControlShadow

ColorUIResource

·

   

primary2

separatorBackground

ColorUIResource

·

   

white

separatorForeground

ColorUIResource

·

   

primary1

subTextFont

FontUIResource

·

   

Abstract

systemTextColor

ColorUIResource

·

   

primary1

systemTextFont

FontUIResource

·

   

Abstract

textHighlightColor

ColorUIResource

·

   

primary3

userTextColor

ColorUIResource

·

   

black

userTextFont

FontUIResource

·

   

Abstract

windowBackground

ColorUIResource

·

   

white

windowTitleBackground

ColorUIResource

·

   

primary3

windowTitleFont

FontUIResource

·

   

Abstract

windowTitleForeground

ColorUIResource

·

   

black

windowTitleInactiveBackground

ColorUIResource

·

   

secondary3

windowTitleInactiveForeground

ColorUIResource

·

   

black

26.6.3.2 Abstract protected methods

As we saw in the previous table, MetalTheme defines six core colors (along with black and white) used to paint all Metal components. Subclasses of MetalTheme must implement the following six methods to define the core colors used by the theme:

protected abstract ColorUIResource getPrimary1( )
protected abstract ColorUIResource getPrimary2( )
protected abstract ColorUIResource getPrimary3( )
protected abstract ColorUIResource getSecondary1( )
protected abstract ColorUIResource getSecondary2( )
protected abstract ColorUIResource getSecondary3( )
26.6.3.3 Black and white

Some interesting (though arguably bizarre) effects could also be created by replacing the values of black and white. These MetalTheme methods return Color.black and Color.white, wrapped as ColorUIResources, by default:

protected ColorUIResource getBlack( )
protected ColorUIResource getWhite( )
26.6.3.4 Additional customization

MetalTheme defines one last method that allows you to use themes to change other aspects of the L&F:

public void addCustomEntriesToTable(UIDefaults table)

Called after the MetalLookAndFeel loads its default tables. The default implementation does nothing. Subclasses can take advantage of this hook to add any number of custom resource values to the defaults table. For example:

public void addCustomEntriesToTable(UIDefaults table) {   table.put("Tree.openIcon", new ImageIcon("open.gif"));   table.put("Tree.closedIcon", new ImageIcon("closed.gif")); }
26.6.3.5 DefaultMetalTheme font properties

DefaultMetalTheme is a concrete extension of MetalTheme; its name property is Steel. It defines the defaults for the six abstract Font properties declared in MetalTheme. Table 26-8 shows the values of these fonts.

Table 26-8. DefaultMetalTheme fonts

Property

Font name

Font style

Font size

controlTextFont

Dialog

Font.BOLD

12

menuTextFont

Dialog

Font.BOLD

12

subTextFont

Dialog

Font.PLAIN

10

systemTextFont

Dialog

Font.PLAIN

12

userTextFont

Dialog

Font.PLAIN

12

windowTitleFont

Dialog

Font.BOLD

12

26.6.3.6 Protected DefaultMetalTheme base colors

We saw earlier that the MetalTheme class defines abstract accessor methods for six protected color resources. Table 26-9 shows the values defined for these colors by the DefaultMetalTheme.[8]

[8] DefaultMetalTheme also defines the package-private properties primary0, secondary0, and secondary4, but no current code seems to use them.

Table 26-9. Protected DefaultMetalTheme colors

Name

Color

primary1

(102, 102, 153)

primary2

(153, 153, 204)

primary3

(204, 204, 255)

secondary1

(102, 102, 102)

secondary2

(153, 153, 153)

secondary3

(204, 204, 204)

The primary colors all have equal parts red and green and a higher blue component. This is how you can tell that these values represent increasingly lighter shades of blue. Similarly, the secondary colors all have the same value for red, green, and blue, indicating that they represent increasingly lighter shades of gray.

As you can see, our simple RedTheme directly touched only three of the properties defined by MetalTheme. (Our previous discussion gave you everything there is to know about making even more drastic changes to the Metal L&F using themes.) All you have to decide is which of the font and color properties you want to change, and then override their respective accessor methods.

Remember, the team that designed the Metal L&F went through a good deal of trouble to make a clean, consistent L&F. As a rule, you're probably best off changing only the fonts and the primary and secondary colors (which define all the other colors). But if you want to, MetalTheme gives you the hooks to define all sorts of different color combinations.

26.6.4 Use of Metal's Client Properties

Another feature that's unique to the Metal L&F is the use of special client properties that allow a handful of components to be customized. To use these properties, you make a call that looks something like this:

myComponent.putClientProperty("JTree.lineStyle", "Angled");

Setting these client properties with any other L&F has no effect, unless it's a custom L&F that knows to look for these properties.

We've touched on most of these properties as they came up throughout the book, so we'll just briefly describe them here. Table 26-10 shows a breakdown of the various client properties used by Metal.[9]

[9] Note that JToolBar used to support an isRollover property, but this has been removed from the table.

Table 26-10. Metal L&F client properties

Component

Property

Data type

Default

Other values

JInternalFrame

isPalette

Boolean

false

true

JScrollBar

isFreeStanding

Boolean

true

false

JSlider

isFilled

Boolean

false

true

JTree

lineStyle

String

"Horizontal"

"Angled", "None"

To create the property key, just build a string from the component type and the property name for example, JScrollBar.isFreeStanding.

JInternalFrame.isPalette

Can be set to Boolean.TRUE to indicate that the border around the internal frame should not be painted.

JScrollBar.isFreeStanding

Set to Boolean.FALSE by the Metal L&F for any scrollbars used by JScrollPanes. This setting just indicates that the scrollbar should be displayed flush against its borders. Normally, there is a small space around the scrollbar. Typically, you won't have reason to change this particular value.

JSlider.isFilled

Can be set to Boolean.TRUE to cause the slider's "track" to be filled on one side to clearly differentiate the space on either side of the "thumb."

JTree.lineStyle

This property can be set to Angled or None (or the default, Horizontal) to change the way the tree structure is drawn. By default, horizontal lines are drawn to separate branches of the tree. Setting this property to Angled causes short angled lines to be drawn between the nodes. As you might guess, setting it to None turns off both features.

26.6.5 Replacement of Individual UI Delegates

Say you're using an L&F that you're basically happy with, but there are a few components you wish had a slightly different appearance. If the changes you want to make can't be done by simply changing resource values, one option is to implement your own custom UI delegates for the components you want to change and then tell the UIManager to use your new classes instead of the L&F defaults.

26.6.5.1 Modifying a scrollbar

We'll show how this can be done with a very simple example. We've decided to toss aside the nice, clean consistent design of the Metal L&F by changing the way the thumb of the scrollbar is displayed. To keep the code as simple as possible, we're going to change the thumb from the Metal style with textured bumps to a simple solid black box.

We do not recommend making such random changes to existing L&Fs. In this particular example, we're breaking something the designers of the Metal L&F worked very hard to achieve consistency. Keep in mind that this is an example that shows you how to do something. We'll leave it to you to have the good sense to know when to do it.

To do this, we need to create our own implementation of the ScrollBarUI class. Rather than starting from scratch, we'll extend MetalScrollBarUI and change only the methods we want to reimplement. In this case, we find that there's a method called paintThumb( ) that's responsible for rendering the thumb of the scrollbar. paintThumb( ) and createUI( ) are the only methods we're going to reimplement. Here's the source code for our new scrollbar UI delegate:

// MyMetalScrollBarUI.java // import java.awt.*; import javax.swing.*; import javax.swing.plaf.*; import javax.swing.plaf.metal.*; // A simple extension of MetalScrollBarUI that draws the thumb as a solid // black rectangle. public class MyMetalScrollBarUI extends MetalScrollBarUI {   // Create our own scrollbar UI!   public static ComponentUI createUI( JComponent c ) {     return new MyMetalScrollBarUI( );   }   // This method paints the scroll thumb. We've just taken the    // MetalScrollBarUI code and stripped out all the   // interesting painting code, replacing it with code that paints a   // black box.   protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)   {     if (!c.isEnabled( )) { return; }     g.translate( thumbBounds.x, thumbBounds.y );     if ( scrollbar.getOrientation( ) == JScrollBar.VERTICAL ) {       if ( !isFreeStanding ) {         thumbBounds.width += 2;       }       g.setColor( Color.black );       g.fillRect( 0, 0, thumbBounds.width - 2, thumbBounds.height - 1 );       if ( !isFreeStanding ) {         thumbBounds.width -= 2;       }     }     else  { // HORIZONTAL       if ( !isFreeStanding ) {         thumbBounds.height += 2;       }       g.setColor( Color.black );       g.fillRect( 0, 0, thumbBounds.width - 1, thumbBounds.height - 2 );       if ( !isFreeStanding ) {         thumbBounds.height -= 2;       }     }     g.translate( -thumbBounds.x, -thumbBounds.y );   } }

Pretty simple stuff. The first thing we did was define a new createUI( ) method. Recall that this is the method JComponent calls when it is assigned a new UI delegate. All we do is return a new instance of our modified scrollbar delegate.

The second method in our class is basically just a stripped-down version of MetalScrollBarUI's paintThumb( ) method. In our implementation, we've removed all the code that created a nice clean thumb, complete with shading and texture bumps, replacing it with single calls to Graphics.fillRect( ).

Since we've extended MetalScrollBarUI, our new scrollbar delegate looks just like the Metal scrollbar, except for the solid black thumb.

The only thing left to do is tell the UIManager to use our custom scrollbar delegate instead of the L&F default. You can probably guess that this is simple. Here's what we do:

UIManager.put("ScrollBarUI", "MyMetalScrollBarUI");

Once we make this call, any new scrollbars that are created use our custom UI delegate instead of the previously installed MetalScrollBarUI. Here's a little test program to prove that this works:

// MetalModExample.java // import javax.swing.*; import java.awt.*; import java.io.*; public class MetalModExample {   public static void main(String[] args) {       // Make sure we're using the Metal L&F, since the example needs it.       try {           UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");       }       catch (Exception e) {           System.err.println("Metal is not available on this platform?!");           e.printStackTrace( );           System.exit(1);       }     JComponent before = makeExamplePane( );     // Replace the MetalScrollBarUI with our own!     UIManager.put("ScrollBarUI", "MyMetalScrollBarUI");     JComponent after = makeExamplePane( );     JFrame f = new JFrame( );     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     Container c = f.getContentPane( );     c.setLayout(new GridLayout(2, 1, 0, 1));     c.add(before);     c.add(after);     f.setSize(450, 400);     f.setVisible(true);   }   // Create a scrollpane with a text area in it.   public static JComponent makeExamplePane( ) {     JTextArea text = new JTextArea( );     try {       text.read(new FileReader("MetalModExample.java"), null);     }     catch (IOException ex) {}     JScrollPane scroll = new JScrollPane(text);     return scroll;   } }

We create two JScrollPanes, which use JScrollBars. The first one is created with the default scrollbar delegate. Then, we tell the UIManager to use our new UI delegate and create a second JScrollPane. Figure 26-12 shows the different scrollbars created by this example.

Figure 26-12. Standard and customized Metal scrollbars
figs/swng2.2612.gif

In this section, we've seen how easy it is to replace a single UI delegate. If you're creating a custom application, and you want to change specific components, this is a nice, easy way to make the changes. However, if you develop a set of custom delegates that you're particularly happy with, you might want to consider rolling them into your own custom L&F, so that you don't have to install each delegate in every program you write. The next section explores your options for creating a custom L&F.



Java Swing
Graphic Java 2: Mastering the Jfc, By Geary, 3Rd Edition, Volume 2: Swing
ISBN: 0130796670
EAN: 2147483647
Year: 2001
Pages: 289
Authors: David Geary

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net