|
Understanding
Borders
Plain or Fancy Decor Not a Problem with Swing By Amy Fowler It's easy to design eye-catching borders for Swing components, thanks to a generic border property that all Swing components inherit.
With equal ease, you can use the border property to surround a component with nothing but a specified amount of white space -- a variety of border that is particularly suitable for text margins. In Version 0.7 of Swing, a new Border interface provides a set of standard Border objects and a generous collection of methods for painting borders, getting border insets (which set the thickness of each of a border's four edges), and determining whether borders are opaque. Swing's Border interface is defined in the swing.border package, along with a set of standard border classes that implement the interface. The Border interface also determines the data types that are used for various kinds of borders. You can get or set a component's border property by calling the following methods, which are inherited from the JComponent class:
|
Swing provides a number of standard borders, and also lets you create your own borders. These are the standard borders that Swing provides:
If none of these predefined borders meets your needs, you can create your own. For example, to place a 3-pixel blue border around the edge of a label, you could do the following:
JLabel label = newJLabel("I'm blue around the edges");label.setBorder(new LineBorder(Color.blue, 3));
As AWT programmers know, the java.awt.Container package defines an insets property that you can use to create padding around the four edges of a container, an area in which a container's children are not allowed to overlap. In Swing, it's still legal to use AWT-style insets, but Swing's border property provides a more streamlined mechanism for placing padding around a component's edges.
In Swing, the AWT insets property is superseded by the insets property in a very simple way: JComponent's getInsets() method simply delegates its operations to a border object's getBorderInsets() method.
In a Swing application, you should not set the insets property directly. Instead, just create an EmptyBorder object, and then set its border property to display whatever amount of white space you want your component to have.
In Swing, it's perfectly legal to invoke getInsets() to determine the amount of space that will be occupied by a border -- a determination that all legal LayoutManager objects should make. But in a Swing program, you should not override getInsets() to return any specific values for your JComponent subclass. Instead, call your border object's getBorderInsets() method.
Swing's Border classes are designed in such a way that a single Border instance can be shared across components; for example, all instantiated JButtons that are equipped with bevel borders can use the same Border object for their bevel.
This strategy works in Swing because the paintBorder() and getBorderInsets() methods take a java.awt.Component as a parameter. This means that a border object can compute any component-specific information on the fly. For example, a border object can dynamically highlight a color property that's based on its parent component's background color.
To facilitate the sharing of Border instances across components, the Swing set provides a special factory class named BorderFactory. The BorderFactory class defines a number of static methods that return shared instances of the various standard border types (whenever sharing is feasible, of course). Because the BorderFactory class is implemented in the swing package, you can set borders without importing the swing.border package.
For example, to set a lowered bevel border for a panel, you could execute the following statements:
JPanel panel = new JPanel();panel.setBorder(BorderFactory. \createLoweredBevelBorder());
We highly recommend using the BorderFactory methods to obtain Border instances -- particularly when you're working with borders that don't have individual properties (such as BevelBorder and EtchedBorder) -- because the BorderFactory methods are designed to optimize the sharing of such objects.
You can get some interesting visual effects by placing multiple border decorations around a single component -- for instance, you might want to find out what happens when you surround a button with both a bevel and some white-space padding around its label.
To help you nest multiple borders inside each other in this fashion, Swing's Border interface provides a CompoundBorder class. The CompoundBorder class takes two Border parameters in its constructor -- one for the outer border and one for the inner border.
For example, the following code fragment creates a label that is surrounded not only by four 10-pixel margins, but also by a surrounding raised-bevel border:
JLabel label = new JLabel("I'm floating");label.setBorder(new CompoundBorder(BorderFactory.createRaisedBevelBorder(),new EmptyBorder(10,10,10,10)));
Some Swing components are equipped with look-and-feel mechanisms that install default borders with appearances which match the L&F characteristics of the platform for which they are designed. Most buttons fall into this category. So do a number of other kinds of classes, including JToolBar, JInternalFrame, and so on.
When a component has a set of default L&F-specific border settings, the component's UI object sets up the component's border when the UI is first installed. This typically happens either when the component is constructed or when the UI is dynamically changed.
But a component's UI will not install its default border if it detects that a non-UI-default border has already been installed. Consequently, when you create a component, you can easily override the installation of a UI-default border for the component by simply setting your own Border object using the setBorder() method.
If you decide to take this approach, though, remember that indiscriminately switching non-UI-default borders for default borders can have detrimental effects on some components' UIs. For example, every Swing button has a UI-default border that can detect the state of the button, and an application can use this ability to start or stop a button animation when the button is pressed. None of Swing's standard Border classes have this ability, so replacing a button's UI-default border with a non-UI-default border can destroy the button's built-in ability to detect and control animations.
Another potentially undesirable side effect stems from the fact that Swing's button and text components provide margins as a component property (for more details, see the getMargins() and setMargins() methods in the appropriate APIs). By calling these methods, a clever Swing programmer can easily get and set the margins of a button component or a text component without bothering with any Border operations at all.
But before you give in to such a temptation, beware: Once again, if a component has a "smart" border and you replace that border with standard Border object, one not smart enough to calculate the component's margins, the margin settings will be lost.
That's one reason that we recommend implementing margins using Border objects. When you follow this advice, you know that your component's UI object will automatically create an appropriate border object (or, alternatively, a CompoundBorder object) that takes the margins property value into account when it interacts with other kinds of objects.
So the moral of this section could be summed up as follows:
"Beware of changing borders on components that have UI-defined borders!"
If none of the standard Border classes meets your needs, don't fret: Swing makes it relatively easy to create your own Border classes. The Swing set does that by providing a base class named AbstractBorder. The AbstractBorder class can be a good starting point for creating customized borders for Swing components.
The following code fragment, for example, defines a custom Border class that paints a double-line border of a specified color:
|
Here are some notes on how the preceding code works:
Oracle is reviewing the Sun product roadmap and will provide guidance to customers in accordance with Oracle's standard product communication policies. Any resulting features and timing of release of such features as determined by Oracle's review of roadmaps, are at the sole discretion of Oracle. All product roadmap information, whether communicated by Sun Microsystems or by Oracle, does not represent a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. It is intended for information purposes only, and may not be incorporated into any contract.
|
| ||||||||||||