Grouping together related components helps organize a user interface and make it easier to learn and use. Java provides several border styles for outlining groups, but none of them match the Mac's recessed style. This article shows how to create a recessed border using special UI defaults available to Mac Java applications.
Table of Contents
- Using standard Java border styles
- Creating the Mac's Aqua recessed border style
- Using UI defaults to get Aqua borders
- Using other borders on non-Macs
- Using recessed borders on tabs
- Handling the quirks of Aqua borders
- Further reading
Using standard Java border styles
To outline a group of components, place them all within a JPanel and call setBorder( ) with a new border object:
JPanel group = new JPanel( ); group.setBorder( new EtchedBorder( ) );
javax.swing.border includes several standard border styles:
- BevelBorder creates a raised or lowered beveled look that went out of GUI style in the 1990s. SoftBevelBorder does the same thing with a slightly softer look. Use these with care (or not at all) or your application will look like it came with Windows 95.
- EtchedBorder creates an "etched" double line border that is widely used in Windows and Linux applications. LineBorder draws a simpler single line border. Both of these are reasonable fallbacks if you don't want to use the Aqua recessed border discussed below.
- MatteBorder creates a thick border by tiling an image or drawing with a color. This is pretty ugly and probably should not be used.
- EmptyBorder creates a border of empty space.
The remaining border classes combine together one or more of the above borders:
- CompoundBorder combines any two of the above borders. One common use combines an EtchedBorder to outline a group and an EmptyBorder to add padding within the border so that the group's components don't touch the border line.
- TitledBorder combines one of the above borders and a text title for the group. It is commonly combined with a LineBorder or EtchedBorder to outline a group and name it.
Here's what these look like:
|Figure 1. Java's BevelBorder (top, raised and lowered) and
SoftBevelBorder (bottom, raised and lowered).
|Figure 2. Java's EtchedBorder (top, raised and lowered) and
|Figure 3. Java's MatteBorder with a color and a repeating image.|
|Figure 4. Java's TitledBorder with an EtchedBorder.|
Creating the Mac's Aqua recessed border style
Swing's look and feel features encapsulate and hide code to draw buttons, check boxes, and other components in different visual styles. For instance, Sun's standard "Metal" look and feel creates a semi-metalic blue appearance for components, while the ancient "Motif" look and feel creates a heavily-beveled style from the 1990s.
On a Mac, the default "Mac OS X" look and feel gives Java applications the Aqua look automatically. It does the right thing for buttons, sliders, and other components, but it won't automatically give you Aqua-style recessed borders. For that you need to do some brief special code.
Using UI defaults to get Aqua borders
Like all look and feels, "Mac OS X" provides a hash table of UI defaults that are used to initialize colors, fonts, icons, borders, and margins for new Swing components. The available defaults differ from one look and feel to another. Apple's "Mac OS X" look and feel includes a few special defaults that enable hidden features not available through the standard Swing API. (See All UI defaults names for common Java look and feels on Windows, Mac OS X, and Linux.)
That "Mac OS X" look and feel defines these two special UI defaults keys:
The value for both of these is a Border object. To get these values, call getBorder( ) on the look and feel's UIDefaults hash table from the UIManager:
Border aquaBorder = UIManager.getBorder( "InsetBorder.aquaVariant" );
And to use the border, simply call setBorder( ) on a JPanel in the usual way:
JPanel group = new JPanel( ); group.setBorder( aquaBorder );
|Figure 5. Apple's inset (left) and titled (right) borders.
The titled border is used as-is here, without a title.
Both Aqua border styles create a recessed look. They differ only in the size of their margins above and inside the group. "InsetBorder.aquaVariant" is intended for use as an untitled group border, while "TitledBorder.aquaVariant" is intended for a titled group.
JPanel titledGroup = new JPanel( ); Border aquaBorder = UIManager.getBorder( "TitledBorder.aquaVariant" ); titledGroup.setBorder( new TitledBorder( aquaBorder, "Title" ) );
|Figure 6. Apple's titled border with a title.|
Note that titled borders using Apple's recessed border automatically put the title above the border, instead of on top of it. This is the correct behavior for group titles on a Mac.
Using other borders on non-Macs
Naturally, if your Java code is intended to run on non-Macs as well, you'll need to revert to some other border style (probably EtchedBorder) if getBorder( ) on UIManager returns null.
JPanel titledGroup = new JPanel( ); Border border = UIManager.getBorder( "TitledBorder.aquaVariant" ); if ( border == null ) border = new EtchedBorder( ); titledGroup.setBorder( new TitledBorder( border, "Title" ) );
Aqua's recessed borders add their own margins, while Java's other borders do not. To compensate, you may need to adjust your inner margins when you use Aqua borders.
Using recessed borders on tabs
JTabbedPane manages a set of tabs that are typically each a JPanel. In Aqua, the tabs line up along the top of a recessed border.
To get this look from Java... do nothing! Apple's "Mac OS X" look and feel automatically creates a recessed border for the tabs. There's nothing more you need to do.
JTabbedPane tabs = new JTabbedPane( ); tabs.addTab( "Tab 1", firstTab ); tabs.addTab( "Tab 2", secondTab ); tabs.addTab( "Tab 3", thirdTab );
|Figure 7. Apple's tabs with a recessed border.|
Handling the quirks of Aqua borders
Aqua recessed borders shade the interior a bit darker than the normal background color. For components with transparent backgrounds, this darker shade shows through just fine. But if you include an opaque component, such as a JPanel, it will draw a block of lighter colored background atop this darker shade and look odd.
The solution is to make components inside the group non-opaque by calling setOpaque( false ):
JPanel innerPanel = new JPanel( ); innerPanel.setOpaque( false );
|Figure 8. Apple's titled border with an opaque (top) and
non-opaque (bottom) inner JPanel.