Tải bản đầy đủ (.pdf) (94 trang)

Java Swing phần 10 pdf

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (684.09 KB, 94 trang )

Java Swing - O’Reilly

- 892 -
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."
JToolBar. isRollover
Can be set to Boolean.TRUE to cause the border on each of the toolbar's buttons to be
replaced with a special rollover border. This border is the same as the button's default
border, except that it is only painted when the cursor is over the button, meaning that at any
given time, at most one button in the toolbar will have a border. Netscape users will
recognize this feature from the toolbars in Navigator.
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.4.5 Replacement of Individual UI Delegates
Say you're using a 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.4.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 taste 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. This, along with createUI(), are the only methods we're
going to reimplement. Here's the source code for our new scrollbar UI delegate.
Java Swing - O’Reilly

- 893 -
// 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 will look just like the Metal
scrollbar, except for the solid black thumb.
Java Swing - O’Reilly

- 894 -
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");

Due to a change in the JDK1.2 class loading scheme, this example does not work properly with JKD1.2
beta4 (it works fine with JDK1.1). To run this example, execute the oldjava interpreter instead of java.
This problem should be cleared up in the next release of JDK1.2.

Once we make this call, any new scrollbars that get created will use our custom UI delegate, instead
of the previously installed MetalScrollBarUI. Here's a little test program that proves that this
works:
// MetalModExample.java
//
import javax.swing.*;

import java.awt.*;
import java.io.*;

public class MetalModExample {
public static void main(String[] args) {
JComponent before = makeExamplePane();

// Replace the MetalScrollBarUI with our own!
UIManager.put("ScrollBarUI", "MyMetalScrollBarUI");

JComponent after = makeExamplePane();

JFrame f = new JFrame();
f.addWindowListener(new BasicWindowMonitor());

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 scroll pane 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.11 shows the different scrollbars created by this example.
Figure 26.11. Standard and customized Metal scrollbars
Java Swing - O’Reilly

- 895 -

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 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.
26.5 Creation of a Custom L&F
Everything we've covered in this chapter up to this point has been useful background information
for the ultimate application customization strategy—creating your own look-and-feel. As you might
guess, this is not something you'll do in an afternoon. However, thanks to the improvements made
in the L&F framework (as of the JDK 1.2 beta 4 release), it's not as difficult as you might think.
You'll likely find that the most difficult part is coming up with graphical design for each
component.
There are basically three different strategies for creating a new L&F:
• Start from ground zero by extending LookAndFeel and extending each of the UI delegates
defined in javax.swing.plaf.

• Extend the BasicLookAndFeel and each of the abstract UI delegates defined in
javax.swing.plaf.basic.
• Extend an existing L&F, like MetalLookAndFeel, and change only selected components.
The first option gives you complete control over how everything works. It also requires a lot of
effort. Unless you are implementing an L&F that is fundamentally different from the traditional
desktop L&Fs, or you have some strong desire to implement your own L&F framework from
scratch, we strongly recommend that you do not use this approach.
The next option is the most logical if you want to create a completely new L&F. This is the
approach we'll focus on in this section. The BasicLookAndFeel has been designed (well, actually
it's been significantly re-designed as of JDK 1.2 beta 4) as an abstract framework for creating new
L&Fs. Each of the Swing L&Fs extends Basic. The beauty of using this approach is that the
Java Swing - O’Reilly

- 896 -
majority of the programming logic is handled by the framework—all you really have to worry about
is how the different components should look.
The third option makes sense if you want to use an existing L&F, but just want to make a few
tweaks to certain components. If you go with this approach, you need to be careful not to do things
that will be confusing to your users. Remember, people expect existing L&Fs to behave in certain
familiar ways.
26.5.1 The PlainLookAndFeel
We'll discuss the process of creating a custom L&F by way of example. In this section, we'll define
bits and pieces of an L&F called PlainLookAndFeel. The goal of this L&F is to be as simple as
possible. We won't be doing anything fancy with colors, shading, or painting—this book is long
enough without filling pages with fancy paint() implementations.
Instead, we'll focus on how to create an L&F. All of our painting will be done in black, white, and
gray, and we'll use simple, single-width lines. It won't be pretty, but we hope it will be educational.
26.5.2 Creating the LookAndFeel Class
The logical first step in the implementation of a custom L&F is the creation of the LookAndFeel
class itself. As we've said, the BasicLookAndFeel serves as a nice starting point. At a minimum,

you'll need to implement the five abstract methods defined in the LookAndFeel base class (none of
which is implemented in BasicLookAndFeel). Here's a look at the beginning's of our custom L&F
class:
// PlainLookAndFeel.java
//
package plain;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;

public class PlainLookAndFeel extends BasicLookAndFeel {
public String getDescription() { return "The Plain Look and Feel"; }
public String getID() { return "Plain"; }
public String getName() { return "Plain"; }
public boolean isNativeLookAndFeel() { return false; }
public boolean isSupportedLookAndFeel() { return true; }
// . . .
}
At this point, we've got an L&F that will actually compile. Let's go a little further and make it
useful. The next major step is to define the defaults for the L&F. This is similar to what we did
earlier when we defined a few custom resources for an application. The difference is that now we
will be defining a complete set of resources for an entirely new L&F that can be used across many
applications. The installation of defaults is handled by getDefaults() which has been broken
down into three additional methods in
BasicLookAndFeel.

Due to a change in the JDK1.2 class loading scheme, the use of custom L&Fs does not work properly
with JDK1.2 beta4 (it works fine with JDK1.1). To use the code shown in this section, execute the

oldjava interpreter instead of java. This problem should be cleared up in the next JDK1.2 release.

Java Swing - O’Reilly

- 897 -
BasicLookAndFeel.getDefaults() creates a UIDefaults table and calls the following three
methods (in this order):
protected void initClassDefaults(UIDefaults table)
protected void initSystemColorDefaults(UIDefaults table)
protected void initComponentDefaults(UIDefaults table)
Let's look at these three steps in detail.
26.5.2.1 Defining Class Defaults
Defining class defaults is the process of enumerating the names of the classes your L&F will use for
each of the UI delegates. One nice feature of the BasicLookAndFeel is that it defines concrete
implementations of all of the UI-delegate classes. One big benefit is that you can test your new L&F
as you're creating it, without having to specify every single delegate class. Instead, just define the
ones you want to test and use the basic implementations for the others. Those that you define (since
they're stored in a simple Hashtable) will override any values previously defined by
BasicLookAndFeel.
A typical implementation of this method looks something like this:
protected void initClassDefaults(UIDefaults table) {
super.initClassDefaults(table); // install the "basic" delegates

String plainPkg = "plain.";
Object[] classes = {
"ProgressBarUI", plainPkg + "PlainProgressBarUI",
"SliderUI", plainPkg + "PlainSliderUI",
"TreeUI", plainPkg + "PlainTreeUI",
// . . . etc
};

table.putDefaults(classes);
}
The first line calls the BasicLookAndFeel implementation, which installs each of the basic UI
delegates. Next, we create a string containing the package name for our L&F classes. This will be
used in constructing the class names of each of our UI delegates. We then create an array of
UIClassID to UI-delegate class name mappings. The items in this array should alternate between
class IDs
[7]
and class names. Include such a mapping for each UI delegate your L&F implements.
[7]
The UIClassID property for all Swing components can be formed by dropping the J from the class name and adding UI at the end. JButton's
UIClassID is ButtonUI, JTree's is TreeUI, etc.
26.5.2.2 Defining L&F Colors
The next set of defaults typically defined are the color resources used by the L&F. You have a lot of
flexibility here in terms of how you handle colors. As we saw earlier in the chapter, the Metal L&F
defines all colors in terms of a color "theme," allowing the colors used by the L&F to be easily
customized. This feature is specific to Metal, but you can implement a similar feature in your own
L&F.
Colors are typically defined according to the colors specified in the java.awt.SystemColor class.
These are the colors used by the BasicLookAndFeel, so if you are going to default any of the
painting routines to Basic, it's important to define values for the system colors. Even if you are
Java Swing - O’Reilly

- 898 -
going to handle every bit of painting in your custom L&F, it's still a good idea, though it is not
required, to use the familiar color names.
BasicLookAndFeel adds another protected method called loadSystemColors(). For non-native
L&Fs, this simply maps an array of color name/color value pairs into resource keys and
ColorUIResource values. For example, a pair of entries in the array might be:
"control", "#FFFFFF"

This would result in a resource called "control" being added, with a value of the color white.

The conversion from #FFFFFF to Color.white is done by the java.awt.Color.decode()
method (which uses java.lang.Integer.decode()). This method takes a string representation of
a color and converts it to a valid Color object. In this case, the # character indicates that we are
specifying a hexadecimal (base-16) number. (You can also use the familiar 0x notation.) The first two
characters (one byte) represent the red component of the color. The next two represent green, and the last
two represent blue. In this example, all values are FF (255 decimal), which maps to the color white.

Using loadSystemColors() allows you to define the color values for your L&F by creating an
array of key/value pairs, like the pair we just looked at. This array is then passed to
loadSystemColors(), along with the UIDefaults table. Here's a sample implementation of
initSystemColorDefaults():
protected void initSystemColorDefaults(UIDefaults table) {
String[] colors = {
"desktop", "#C0C0C0",
"activeCaption", "#FFFFFF",
"activeCaptionText", "#000000",
"activeCaptionBorder", "#000000"
// more of the same . . .
};
loadSystemColors(table, colors, false);
}
Table 26.9 shows the 26 color keys defined by SystemColor and used by the BasicLookAndFeel.
Table 26.9, Standard System Color Properties
System Color Property Description
desktop
Color of the desktop background
activeCaption
Color of the titlebar (captions) when the frame is active

activeCaptionText
Color of the titlebar text when the frame is active
activeCaptionBorder
Color of the titlebar border when the frame is active
inactiveCaption
Color of the titlebar (captions) when the frame is inactive
inactiveCaptionText
Color of the titlebar text when the frame is inactive
inactiveCaptionBorder
Color of the titlebar border when the frame is inactive
window
Color of the interior of the window
windowBorder
Color of the window border
windowText
Color of the window text
menu
Background color of menus
menuText
Color of the text in menu items
text
Background color of editable text
textText
Color of editable text
textHighlight
Background color of editable text when highlighted
Java Swing - O’Reilly

- 899 -
textHighlightText

Color of editable text when highlighted
textInactiveText
Color of normally editable text that has been disabled.
control
Standard color for controls such as buttons or scrollbar thumbs
controlText
Color for text inside controls
controlHighlight
Highlight color for controls
controlLtHighlight
Lighter highlight color for the controls
controlShadow
Shadow color for controls
controlDkShadow
Darker shadow color for the controls
scrollbar
Color to use for the background area of a scrollbar (where the thumb slides)
info
Background color for informational text
infoText
Color for informational text
26.5.2.3 Defining Component Defaults
The last method called by BasicLookAndFeel.getDefaults() is initComponentDefaults().
This is where you define all of the colors, icons, borders, and other resources used by each of the
individual component delegates. The BasicLookAndFeel implementation of this method defines
over 300 different resource values for 40 delegate classes. We've cataloged this long list of
resources, along with the type of value expected for each, in Appendix A.
The good news is that you don't have to redefine all 300+ resource values in your custom L&F,
though you certainly can if you want. Many of the resources are colors and are defined in terms of
the system colors we've already defined. For example, the Button.background resource defaults to

the value defined for "control" while Button.foreground defaults to "controlText". As long
as you've defined values for the system colors and you're happy with the mappings to these system
colors defined by the BasicLookAndFeel, you can get by with little or no changes to the
component-level color resources. The amount of customization done in this method is really up to
you. If you like the resource choices made by the BasicLookAndFeel, use them. If you want your
own custom defaults, you can change them.
There are a few useful steps used by the Swing L&Fs that will make the implementation of this
method easier to understand. We'll discuss these in the following.
Define Fonts
Chances are there's a fixed set of fonts your L&F will use throughout its delegates. It's a
good idea to define these up-front, so you're not creating duplicate font resources throughout
the method. Recall from earlier in the chapter that resources defined by the L&F should
implement the
UIResource interface, so we'll create our fonts as FontUIResource objects.
You might choose to define a few fonts like this:
FontUIResource sansSerifPlain10 =
new FontUIResource("SansSerif", Font.PLAIN, 10);
FontUIResource monospacedPlain10 =
new FontUIResource("Monospaced", Font.PLAIN, 10);
Define Colors
If you plan to use colors not defined by the system colors, and you're not using a flexible
color strategy like Metal's themes, remember to define these colors as ColorUIResources.
For example:
ColorUIResource green = new ColorUIResource(Color.green);
ColorUIResource veryLightGray = new ColorUIResource(240, 240, 240);
Java Swing - O’Reilly

- 900 -
Define Insets
Several of the resource values are defined as java.awt.Insets. Again, it's convenient to

define these values up-front. For example:
InsetsUIResource zeroInsets = new InsetsUIResource(0,0,0,0);
InsetsUIResource bigInsets = new InsetsUIResource(10,10,10,10);
Define Borders
If you're going to use the standard Swing borders for your components, recall that you can
obtain singleton resource borders from the BorderUIResource class. For example:
Border etchedBorder = BorderUIResource.getEtchedBorderUIResource();
Border blackLineBorder = BorderUIResource.getBlackLineBorderUIResource();
This works great for defining simple borders. However, it's often useful to define dynamic
borders that change based on the state of the component they are bordering. For example,
when a button is pressed, it often draws its border differently than when it is in the default
raised position. The Basic L&F provides a class called BasicBorders that includes inner
classes for several common dynamic borders. We'll cover this class at the end of the chapter.
Define Icons
Several components define a variety of Icon resources. There are two distinct types of icons
you'll want to define: static and dynamic. Static icons are usually (though not always)
ImageIcons, loaded from small GIF files. They are used for things like tree nodes and
JOptionPane dialogs. It's generally a good idea to define static icons using the
UIDefaults.LazyValue class (discussed earlier in the chapter) to avoid loading the images
in applications that don't use the components they are associated with. The easiest strategy is
just to use LookAndFeel.makeIcon() method, which returns LazyValue objects, to load an
icon from the location of your L&F classes. For example, to load an image called
warning.gif from the icons directory directly under the directory containing your L&F
classes, you would use the following code:
Object warningIcon = LookAndFeel.makeIcon(getClass(),
"icons/warning.gif");
Table 26.10 summarizes the default icons loaded by BasicLookAndFeel. If you use the
default resource values for these icons, be sure to supply an image for each of the icons (in
the icons subdirectory). No default image files are defined.
Table 26.10, Image Icons Defined by BasicLookAndFeel

Resource Name Filename
FileChooser.detailsViewIcon DetailsView.gif
FileChooser.homeFolderIcon HomeFolder.gif
FileChooser.listViewIcon ListView.gif
FileChooser.newFolderIcon NewFolder.gif
FileChooser.upFolderIcon UpFolder.gif
FileView.computerIcon Computer.gif
FileView.directoryIcon Directory.gif
FileView.fileIcon File.gif
FileView.floppyDriveIcon FloppyDrive.gif
Java Swing - O’Reilly

- 901 -
FileView.hardDriveIcon HardDrive.gif
InternalFrame.icon JavaCup.gif
OptionPane.errorIcon Error.gif
OptionPane.informationIcon Inform.gif
OptionPane.questionIcon Question.gif
OptionPane.warningIcon Warn.gif
Tree.closedIcon TreeClosed.gif
Tree.leafIcon TreeLeaf.gif
Tree.openIcon TreeOpen.gif
It's more challenging to define icons that change based on the state of a component. The
most obvious examples of dynamic icons are radio buttons and checkboxes. These icons
paint themselves differently depending on whether or not they are selected and, typically,
whether or not they are currently being pressed. We'll look at a strategy for implementing
dynamic icons in a few pages.
Define Other Resources
A variety of other resources, including Dimensions and Integer values can also be defined
as component resources. Remember, you can refer to Appendix A for a complete list.

Create Defaults Array
Now that you've defined all the common resources that might be shared by multiple
components, it's time to put together an array of key/value pairs for the resources you want
to define. This array is typically handled just like the others we've seen up to this point—
entries in the array alternate between resource keys and values. Since there are potentially a
very large number of resources being defined here, it's a good idea to group resources by
component. Here's part of our PlainLookAndFeel defaults array definition:
Object[] defaults = {
"Button.border", buttonBorder,
"Button.margin", new InsetsUIResource(2, 2, 2, 2),
"Button.font", sansSerifPlain10,

"RadioButton.icon", radioButtonIcon,
"RadioButton.pressed", table.get("controlLtHighlight"),
"RadioButton.font", sansSerifPlain10,

"CheckBox.icon", checkBoxIcon,
"CheckBox.pressed", table.get("controlLtHighlight"),
"CheckBox.font", sansSerifPlain10,

"Slider.foreground", table.get("controlText")
};
Note that you aren't limited to the resources listed in Appendix A. In Table 26.10, we've
added two custom resources called RadioButton.pressed and CheckBox.pressed which
we'll use as background colors when the button is being pressed.
Two Little Details
We've covered almost everything you have to think about when implementing
initComponentDefaults(). There are two last important steps (one at the beginning and
Java Swing - O’Reilly


- 902 -
one at the end) to remember. The first thing you will typically do is call
super.initComponentDefaults(). This loads all of the defaults defined by
BasicLookAndFeel. If you don't do this, you are likely to have a runtime error when the
BasicLookAndFeel tries to access some undefined resource. Of course, if you define all of
the resources in your L&F, you don't have to make the super call. The last thing to do in
your method is to load your defaults into the input UIDefaults table. When it's complete,
the initComponentDefaults() method should look something like this:
protected void initComponentDefaults(UIDefautls table) {
super.initComponentDefaults(table);

// Define any common resources, lazy/active value resources, etc . . .

Object[] defaults = {
// . . . define all the defaults
};

table.putDefaults(defaults);
}


26.5.3 Defining an Icon Factory
This is not a required step, but it can prove to be a useful one. The Swing L&Fs group the
definitions of various dynamic icons into an icon factory class. This class serves as a holder of
singleton instances of the various dynamic icons used by the L&F and also contains the inner
classes that actually define the icons.
Which icons, if any, you define in an icon factory is up to you. The Metal L&F uses its icon factory
to draw all of its icons, except those used by JOptionPane. This allows Metal to change the color
of its icons based on the current color theme, a task not easily achieved if the icons are loaded from
GIF files.

For our purposes, we'll concentrate on defining dynamic icons. The PlainLookAndFeel will use
GIFs for all of the static icons.
The easiest way to understand how to implement a dynamic icon is to look at a simple example.
Here's a trimmed-down version of our PlainIconFactory class, showing how we implemented the
radio button icon:
//


PlainIconFactory.java
//
package plain;

import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.*;
import java.io.Serializable;

public class PlainIconFactory implements Serializable {
private static Icon radioButtonIcon;
private static Icon checkBoxIcon; // implemention trimmed from example

Java Swing - O’Reilly

- 903 -
// Provide access to the single RadioButtonIcon instance.
public static Icon getRadioButtonIcon() {
if (radioButtonIcon == null) {
radioButtonIcon = new RadioButtonIcon();
}
return radioButtonIcon;

}

// An icon for rendering the default radio button icon.
private static class RadioButtonIcon implements Icon, UIResource, Serializable
{
private static final int size = 15;

public int getIconWidth() { return size; }
public int getIconHeight() { return size; }

public void paintIcon(Component c, Graphics g, int x, int y) {

// Get the button & model containing the state we are supposed to show
AbstractButton b = (AbstractButton)c;
ButtonModel model = b.getModel();

// If the button is being pressed (& armed), change the BG color
// (NOTE: Could also do something different if the button is disabled)

if (model.isPressed() && model.isArmed()) {
g.setColor(UIManager.getColor("RadioButton.pressed"));
g.fillOval(x, y, size-1, size-1);
}

// Draw an outer circle
g.setColor(UIManager.getColor("RadioButton.foreground"));
g.drawOval(x, y, size-1, size-1);

// Fill a small circle inside if the button is selected
if (model.isSelected()) {

g.fillOval(x+4, y+4, size-8, size-8);
}
}
}
}
We provide a static getRadioButtonIcon() method that creates the icon the first time it's called.
On subsequent calls, the single instance is returned immediately. We'll do the same thing for each
dynamic icon we define. Next, we have the RadioButtonIcon inner class. Recall from Chapter 4,
that there are three methods involved in implementing the
Icon interface (the other interfaces,
UIResource and Serializable, have no methods). Our implementations of getIconWidth() and
getIconHeight() are simple; they just return a constant size.
The interesting code is in paintIcon(). In this method, what we paint depends on the state of the
button's model. In our implementation, we do two checks. First, we check to see if the button is
being pressed. If so (and if the button is armed, meaning that the mouse pointer is still over the
button), we paint a special background color. Then we paint a solid outer circle and perform the
second check. This check is to see if the button is selected. If it is, we paint a solid circle inside the
outer circle.
One thing to note here is that we chose to define a custom resource called RadioButton.pressed.
Since there is no standard policy for showing that a button is pressed, we use this resource to define
the background for our pressed button.
Java Swing - O’Reilly

- 904 -
The really interesting thing about this new icon class is that for many L&Fs, defining this icon is all
you need to do to for the delegate that uses it. In PlainLookAndFeel, we don't even define a
PlainRadioButtonUI class at all. Instead, we just create a RadioButtonIcon and set it as the icon
using the resource "RadioButton.icon". Figure 26.12 shows some RadioButtons using the
PlainLookAndFeel. The first button is selected, the second is selected and is being held down, and
the third is unselected.

Figure 26.12. PlainIconFactory.RadioButtonIcon

26.5.4 Defining Custom Borders
Certain Swing components are typically rendered with some type of border around them. The
javax.swing.border package defines a number of static borders that you can use. However, it's
often desirable to create your own custom borders as part of your L&F. Also, certain borders (just
like certain icons) should be painted differently depending on the state of the object they are being
painted around.
The Swing L&Fs define custom borders in a class called <L&FName>Borders. Many of the inner
classes defined in BasicBorders may be useful when defining your own L&F. These are the
borders used by default by the BasicLookAndFeel. They include the following inner classes:
public static class ButtonBorder extends AbstractBorder implements UIResource
public static class FieldBorder extends AbstractBorder implements UIResource
public static class MarginBorder extends AbstractBorder implements UIResource
public static class MenuBarBorder extends AbstractBorder implements UIResource
public static class RadioButtonBorder extends ButtonBorder
public static class SplitPaneBorder implements Border, UIResource
public static class ToggleButtonBorder extends ButtonBorder
It's probably not too important to understand the details of most of these inner classes. The
important thing to know is that these are the borders installed for certain components by the
BasicLookAndFeel.
One of these inner classes, MarginBorder, does deserve special mention. This class defines a
border that has no appearance, but takes up space. It's used with components that define a margin
property, specifically AbstractButton, JToolBar, and JTextComponent. When defining borders
for these components, it's important to create a CompoundBorder that includes an instance of
BasicBorders.MarginBorder. If you don't do this, your L&F will ignore the component's margin
property, a potentially confusing problem for developers using your L&F. Here's an example from
PlainLookAndFeel in which we use a MarginBorder to define the border that we'll use for our
JButtons:
Border marginBorder = new BasicBorders.MarginBorder();

Object buttonBorder = new BorderUIResource.CompoundBorderUIResource(
new PlainBorders.ButtonBorder(), marginBorder);
Java Swing - O’Reilly

- 905 -
Note that the MarginBorder constructor takes no arguments. It simply checks the component's
margin property in its paintBorder() method. Using a MarginBorder with a component that has
no margin property simply results in a border with insets of (0,0,0,0).
This example brings us back to the idea of creating a PlainBorders class that defines a set of
borders for our L&F. Keep in mind that you don't have to do this. You're free to use the default
borders provided by Basic, or even to use the simple borders defined by the swing.border
package. Here's the PlainBorders class in which we define a single inner class for handling button
borders:
//


PlainBorders.java
//
package plain;

import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.*;

public class PlainBorders {
// An inner class for JButton borders.
public static class ButtonBorder extends AbstractBorder implements UIResource
{
private Border raised; // use this one by default

private Border lowered; // use this one when pressed

// Create the border.
public ButtonBorder() {
raised = BorderFactory.createRaisedBevelBorder();
lowered = BorderFactory.createLoweredBevelBorder();
}

// Define the insets (in terms of one of the others).
public Insets getBorderInsets(Component c) {
return raised.getBorderInsets(c);
}

// Paint the border according to the current state.
public void paintBorder(Component c, Graphics g, int x, int y,
int width, int height) {

AbstractButton b = (AbstractButton)c;
ButtonModel model = b.getModel();

if (model.isPressed() && model.isArmed()) {
lowered.paintBorder(c, g, x, y, width, height);
}
else {
raised.paintBorder(c, g, x, y, width, height);
}
}
}
}
For the sake of providing a very simple example, we've implemented our ButtonBorder class using

two other existing borders. Which of these borders is actually painted by our border is determined
by the state of the button model.
Java Swing - O’Reilly

- 906 -
26.5.5 The BasicGraphicsUtils Class
There's one more class from the Basic L&F worth knowing something about. BasicGraphicsUtils
defines a number of static utility methods that might be useful when creating your own L&F.
26.5.5.1 Methods
public static void drawBezel(Graphics g, int x, int y, int w, int h, boolean isPressed,
boolean isDefault, Color shadow, Color darkShadow, Color highlight)
public static void drawDashedRect(Graphics g, int x, int y, int width, int height)
public static void drawEtchedRect(Graphics g, int x, int y, int w, int h, Color control, Color
shadow, Color darkShadow, Color highlight)
public static void drawGroove(Graphics g, int x, int y, int w, int h, Color shadow, Color
highlight)
public static void drawLoweredBezel(Graphics g, int x, int y, int w, int h, Color shadow,
Color darkShadow, Color highlight)
These methods can be used to draw various different rectangles. The Basic L&F uses these
for many of its borders. Figure 26.13 shows several rectangles created by these methods.
The parameters shown in the four drawBezel() examples correspond to isPressed and
isDefault, respectively
Figure 26.13. BasicGraphicsUtils

public static void drawString(Graphics g, String text, int underlinedChar, int x, int y)
Draws a String at the specified location. The first occurrence of underlinedChar will be
underlined. This is typically used to indicate mnemonics.
public static Insets getEtchedInsets()
public static Insets getGrooveInsets()
Return the Insets used by the drawEtchedRect() and drawGroove() methods.

public static Dimension getPreferredButtonSize(AbstractButton b, int textIconGap)
Returns the preferred size of a button based on its text, icon, insets, and the textIconGap
parameter.
Java Swing - O’Reilly

- 907 -
26.5.6 Create the Individual UI Delegates
The key step in developing a unique L&F is the creation of a set of UI delegate classes for the
various Swing components that can't be sufficiently customized just by setting resource values or
defining custom borders and icons.
Unfortunately, a description of the methods involved in each individual UI delegate is beyond the
scope of this book (we're starting to fear that no one will be able to lift it as it is!). Still, we don't
want to leave you in the dark after coming so far, so we'll take a detailed look at a single example.
For the rest of the chapter, we'll focus on the creation of the PlainSliderUI , but many of the steps
along the way will apply to other delegates as well.
26.5.6.1 Define a Constructor
Constructors for UI delegates don't typically do much. The main thing to concern yourself with is
whether or not you want to keep a reference to the component the delegate is rendering. Generally
speaking, this is not necessary because the component will always be passed as a parameter to
methods on the delegate. However, if you're extending Basic, you do need to pay attention to the
requirements of the Basic constructor. In the case of
BasicSliderUI, we are required to pass a
JSlider as an argument, but since BasicSliderUI just ignores it anyway, we just pass a null.
Here's the beginning of our PlainSliderUI class:
// PlainSliderUI.java
//
package plain;
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.*;

import javax.swing.plaf.basic.*;

public class PlainSliderUI extends BasicSliderUI {
//
public PlainSliderUI() {
super(null); // basic ignores the parameter anyway
}
//
}



26.5.6.2 Define the Factory Method
The next important step is to define a createUI() factory method. This is how the delegate gets
created for a given component. Typically, all you need to do here is return a new instance of your
UI delegate class. In some cases, it may be better to use a single instance of the UI delegate class for
all components, then createUI() will always return the same static object, rather than creating a
new one each time. If you go with this approach, make sure your delegate (including its superclass)
doesn't hold any instance-specific component data.
In PlainSliderUI, our createUI() method just returns a new instance of PlainSliderUI:
public static ComponentUI createUI(JComponent c) {
return new PlainSliderUI();
}
Java Swing - O’Reilly

- 908 -
26.5.6.3 Define installUI() and uninstallUI() (optional)
The installUI() and uninstall() methods give you an opportunity to initialize your UI delegate
with information from the component it's rendering. Both methods take a single JComponent
parameter, which can safely be cast to the appropriate type if needed.

If you're not extending the Basic L&F, you'll typically have quite a bit of work to do in the
installUI() method. On the other hand, if you are taking advantage of the BasicLookAndFeel,
you'll have little (if anything) to do here. In the case of SliderUI, the BasicSliderUI.install()
method does the following things:
• Makes the slider opaque
• Adds six listeners to the slider to track its state
• Retrieves resource defaults from the UIManager
• Installs the border, background, and foreground colors (from resource values) on the
component
• Adds keyboard actions to the slider, allowing it to be adjusted with keys as well as with a
mouse
• Defines a Timer used when scrolling
• Calculates the bounds of each region of the slider for use in painting
We list these items to give you an idea of the types of things typically done in installUI(). In
PlainSliderUI, we don't bother reimplementing this method, since the default does everything we
need.
The uninstall() method should undo anything done by installUI(). In particular, any listeners
should be removed. BasicSliderUI.uninstall() does the following for us:
• Removes the border
• Stops the Timer
• Uninstalls the listeners
• Sets fields to null
Again, we chose not to override
uninstallUI() in PlainSliderUI.
26.5.6.4 Define Component Size
Recall that the ComponentUI base class defines the three standard sizing methods:
getMinimumSize() , getMaximumSize(), and getPreferredSize(). Depending on the
component, and on how much you are going to customize your L&F, you may or may not need to
worry about implementing these methods. Also, some of the implementations of these methods are
broken down into several additional methods.

In the case of BasicSliderUI, the preferred and minimum size methods are broken down into
pairs, based on the orientation of the slider. The following four methods are used:
public Dimension getMinimumHorizontalSize()
public Dimension getMinimumVerticalSize()
public Dimension getPreferredHorizontalSize()
public Dimension getPreferredVerticalSize()
Java Swing - O’Reilly

- 909 -
If you want to change the preferred or minimum size of the slider, these methods can be overridden.
In PlainSliderUI, we do the following:
private static final Dimension PREF_HORIZ = new Dimension(250, 15);
private static final Dimension PREF_VERT = new Dimension(15, 250);
private static final Dimension MIN_HORIZ = new Dimension(25, 15);
private static final Dimension MIN_VERT = new Dimension(15, 25);

public Dimension getPreferredHorizontalSize() {
return PREF_HORIZ;
}
public Dimension getPreferredVerticalSize() {
return PREF_VERT;
}
public Dimension getMinimumHorizontalSize() {
return MIN_HORIZ;
}
public Dimension getMinimumVerticalSize() {
return MIN_VERT;
}
26.5.6.5 Override Component-Specific Details
So far, we've laid most of the groundwork for creating the custom UI delegate. The next thing is to

look for any little details the Basic delegate allows you to customize. This will, of course, vary
greatly from component to component. For sliders, the following two methods allow us to specify
how large certain parts of the slider will be. The values returned by these methods are used in
various calculations.
protected Dimension getThumbSize()
protected int getTickLength()
In PlainSliderUI, we provide the following implementations of these methods:
// Define the size of the thumb.
protected Dimension getThumbSize() {
Dimension size = new Dimension();

if (slider.getOrientation() == JSlider.VERTICAL) {
size.width = 10;
size.height = 7; // needs to be thick enough to be able to grab it
}
else {
size.width = 7; // needs to be thick enough to be able to grab it
size.height = 10;
}
return size;
}
// How big are major ticks?
protected int getTickLength() {
return 6;
}
There are quite a few other methods that revolve around calculating various sizes, but the defaults
for these methods will serve us well enough.
26.5.6.6 Paint the Component
Java Swing - O’Reilly


- 910 -
At last, the fun part! When all is said and done, the reason you create your own L&F is to be able to
paint the components in your own special way. As you might guess, this is where the paint()
method comes in. However, if you had to implement paint() from scratch, you'd have to deal with
a lot of details that are the same for all L&Fs. Luckily, the Basic L&F has matured over time into a
nice, clean framework with lots of hooks to allow you to customize certain aspects of the display,
without worrying about every little detail.
Turning our attention to our slider delegate, we find that the BasicSliderUI's paint() method is
broken down into five other methods:
public void paintFocus(Graphics g)
public void paintLabels(Graphics g)
public void paintThumb(Graphics g)
public void paintTicks(Graphics g)
public void paintTrack(Graphics g)
These methods let us paint the specific pieces of the slider that we want to control, without
having to deal with the things we don't want to change. In PlainSliderUI, we've chosen to
implement only paintThumb() and paintTrack().
The paintFocus() method in BasicSliderUI paints a dashed rectangle around the slider when it
has focus. This is reasonable default behavior for our L&F. The paintLabels() method takes care
of painting the optional labels at the correct positions, and paintTicks() draws all the little tick
marks. We have influenced how this method works by overriding the getTickLength() method.
The BasicSliderUI.paintTicks() method uses this length for major ticks and cuts it in half for
minor ticks. If we didn't like this strategy, we could override paintTicks(). Better still, we could
override the four methods it uses:
protected void paintMajorTickForHorizSlider(Graphics g, Rectangle tickBounds, int x)
protected void paintMajorTickForVertSlider(Graphics g, Rectangle tickBounds, int y)
protected void paintMinorTickForHorizSlider(Graphics g, Rectangle tickBounds, int x)
protected void paintMinorTickForVertSlider(Graphics g, Rectangle tickBounds, int y)
These methods allow us to paint each tick any way we want, without having to do the
calculations performed by

paintTicks(). It's important to look for methods like this in
each UI delegate that you implement—they can be major time-savers.
Back to PlainSliderUI. As we said, we've chosen to implement only two of the methods paint()
uses, making our
PlainSliderUI as simple as possible. The first of these methods is
paintTrack(). This is where we paint the line that the slider thumb will slide along. In fancier
L&Fs, this is made up a various lines and rectangles that create a nicely shaded track. Here's our
much simpler implementation:
// Paint the track as a single solid line.
public void paintTrack(Graphics g) {
int x = trackRect.x;
int y = trackRect.y;
int h = trackRect.height;
int w = trackRect.width;

g.setColor(slider.getForeground());

if (slider.getOrientation() == JSlider.HORIZONTAL) {
g.drawLine(x, y+h-1, x+w-1, y+h-1);
Java Swing - O’Reilly

- 911 -
}
else {
g.drawLine(x+w-1, y, x+w-1, y+h-1);
}
}
We've chosen to draw a single line, using the slider's foreground color, along the bottom of the
available bounds defined by trackRect. You're probably wondering where this trackRect variable
came from. This is a protected field defined in BasicSliderUI that keeps track of the area in which

the slider's track should be painted. There are all sorts of protected fields like this in the Basic L&F.
The next slider painting method we've implemented is paintThumb(). Given our simple painting
strategy, it actually looks surprisingly like paintTrack().
// Paint the thumb as a single solid line, centered in the thumb area.
public void paintThumb(Graphics g) {
int x = thumbRect.x;
int y = thumbRect.y;
int h = thumbRect.height;
int w = thumbRect.width;

g.setColor(slider.getForeground());
if (slider.getOrientation() == JSlider.HORIZONTAL) {
g.drawLine(x+(w/2), y, x+(w/2), y+h-1);
}
else {
g.drawLine(x, y+(h/2), x+w-1, y+(h/2));
}
}
Here, we use another protected field called thumbRect to determine where we're supposed to paint
the thumb. Recall from our getThumbSize() method, that we set the thumb width (or height for
horizontal sliders) to 7. However, we only want to paint a single short line, centered relative to the
total width. This is why you see (w/2) and (h/2) as part of the calculations.
26.5.7 Don't Forget to Use It
The last step is to make sure our PlainLookAndFeel actually uses this nice new class. All we have
to do is add a line to the array we've created in the initClassDefaults() method of
PlainLookAndFeel. Since this is the only custom delegate we've created, our implementation of
this method looks like this:
protected void initClassDefaults(UIDefaults table) {
super.initClassDefaults(table); // install the "basic" delegates


String plainPkg = "plain.";

Object[] classes = {
"SliderUI", plainPkg + "PlainSliderUI"
};

table.putDefaults(classes);
}
Java Swing - O’Reilly

- 912 -
26.5.8 How's It Look?
That just about covers it for our PlainSliderUI. Let's take a look at a few "plain" sliders and see
how it turned out. Figure 26.14 shows four sliders with different tick settings, labels, and
orientations.
Figure 26.14. PlainSliderUI

26.5.9 One Down
Creating a custom look-and-feel is not a trivial task. As we've said, it's beyond the scope of this
book to get into the details of every UI delegate. What we've tried to do, instead, is to give you an
idea of the general procedure for implementing component-specific delegates by extending the
Basic L&F. The remaining steps can be described very loosely as "repeat until done." Some of the
other components will be easier to deal with than the slider, and some will be more challenging. In
any case, this section has introduced the core ideas you need to implement the rest of the UI
delegates.
Chapter 27. Swing Utilities
There are many tasks you run into that are common and not terribly difficult. Hence, they get
rewritten several times in several small variations. Ideally, you would code the task up into a
method or class, and keep it around for reuse later. That's exactly what happened with the
SwingUtilities class. It contains several dozen methods to handle some tasks you encounter

when dealing with real applications. (There's a method to test if the mouse button pressed was the
left mouse button, for example.)
And how many simple timer classes exist that fire events at useful intervals? Or more likely, how
many methods out there contain while() loops wrapped around Thread.sleep(delay) calls to do
the work of a clock? The Timer class in the Swing package provides a flexible timer for all your
timing needs.
If you use icons, you have probably had to create a "disabled" version of the image for use in your
application. Now, the
GrayFilter class can help you out. There's even a tooltip manager class
devoted to handling all of the common functions you might expect to have available for tooltips.
Java Swing - O’Reilly

- 913 -
We've broken these utilities up into several categories, more for presentation than anything. You'll
find the SwingUtilities, SwingConstants, Timer, and ToolTipManager classes in Section 27.1.
Section 27.3 covers the KeyStroke and EventListenerList classes. "Editing Utilities" looks at the
CellEditor and CellEditorListener interfaces as well as the DefaultCellEditor and
KeyStroke classes. And Section 27.4 covers the GrayFilter class.
Figure 27.1 shows the classes covered in this chapter:
Figure 27.1. Class hierarchy for the utility classes


27.1 General Utilities
The general utilities presented here are meant for use with any part of your application. The static
methods of the SwingUtilities class are called throughout the Swing source code and will
probably be useful to you as well. You'll find a lot of these utilities fairly straightforward, and
maybe even easy to reproduce with your own code. But try to familiarize yourself with these APIs;
they're meant to keep you from reinventing wheels with each new application you write.
27.1.1 The SwingUtilities Class
This class serves as a collection point for several methods common in more advanced GUI

development projects. You probably won't use all of the methods in any one application, but some
of the methods will doubtless come in handy from time to time. While the purpose of many of these
methods will be obvious from their signatures, here's a brief description of the utility calls at your
disposal. If you want to see a more detailed discussion of the
invokeLater() and
invokeAndWait() methods, check out Chapter 28.
27.1.1.1 Constructor
public SwingUtilities()
The constructor for SwingUtilities is public, but all of the public methods are static, so
you will not need to create an instance.
27.1.1.2 Class Methods
public static Rectangle[] computeDifference(Rectangle rectA, Rectangle rectB)
Java Swing - O’Reilly

- 914 -
Returns the regions in rectA that do not overlap with rectB. If rectA and rectB do not
overlap at all, an empty array is returned. The number of rectangles returned depends on the
nature of the intersection.
public static Rectangle computeIntersection(int x, int y, int width, int height, Rectangle
dest)
Returns the intersection of two rectangles (the first represented by (x, y, width, height)),
without allocating a new rectangle. Instead, dest is modified to contain the intersection and
then returned. This can provide a significant performance improvement over the similar
methods available directly through the Rectangle class, if you need to do several such
intersections.
public static int computeStringWidth(FontMetrics fm, String str)
Given a particular font metrics, this method returns the pixel length of the string str.
public static Rectangle computeUnion(int x, int y, int width, int height, Rectangle dest)
Returns the union of the rectangle represented by (x, y, width, height) and dest. As with
computeIntersection(), dest is modified and returned; no new Rectangle object is

allocated.
public static MouseEvent convertMouseEvent(Component source, MouseEvent
sourceEvent, Component destination)
Returns a new MouseEvent, based on sourceEvent with the (x, y) coordinates translated to
destination's coordinate system and the source of the event set as destination, provided
destination is not null. If it is, source is set as the source for the new event. The actual
translation of (x, y) is done with convertPoint().
public static Point convertPoint(Component source, Point aPoint, Component destination)
public static Point convertPoint(Component source, int x, int y, Component destination)
Convert a point from the source coordinate system to the destination coordinate system. If
either source or destination is null, the other component's root component coordinate
system will be used. If both are null, the point is returned untranslated.
public static void convertPointFromScreen(Point p, Component c)
Converts a point on the screen, p, to a coordinate relative to the upper-lefthand corner of the
component, c.
public static Component getRoot(Component c)
Returns the parent Window component, or the last applet to contain c if it is in a browser
environment.
public static void convertPointToScreen(Point p, Component c)
Java Swing - O’Reilly

- 915 -
Opposite of the previous method, this method takes a point, p, relative to the upper-left
corner of the component, c, and converts it to a coordinate on the screen. Such a conversion
makes light work of tiling popup windows.
public static Rectangle convertRectangle(Component source, Rectangle aRectangle,
Component destination)
Translates aRectangle from the source coordinate system to the destination coordinate
system, following the same rules as convertPoint().
public static Component findFocusOwner(Component c)

Returns the component at or below c that has the keyboard focus, if any. Because of security
restrictions, this may not work for non-Swing components in applets.
public static Accessible getAccessibleAt(Component c, Point p)
Returns the Accessible component at point p (relative to the component c). If no such
component exists, null is returned.
public static Accessible getAccessibleChild(Component c, int i)
Returns the ith accessible child of component c that implements the Accessible interface.
public static int getAccessibleChildrenCount(Component c)
Returns the number of accessible childern in component c that implements the Accessible
interface.
public static int getAccessibleIndexInParent(Component c)
For a given component c, this method returns its index in its accessible parent. If the
component does not have an accessible parent, -1 is returned.
public static AccessibleStateSet getAccessibleStateSet(Component c)
Returns the set of accessible states active for the component c.
public static Container getAncestorNamed(String name, Component comp)
Returns the first container named name that contains component comp. null is returned if
name cannot be found.
public static Container getAncestorOfClass(Class c, Component comp)
Similar to getAncestorNamed(), this method returns the first container that is an instance
of class c that contains component comp.
public static Component getDeepestComponentAt(Component parent, int x, int y)
Java Swing - O’Reilly

- 916 -
Performs a recursive search through the component hierarchy starting at parent, and returns
the last component containing the point (x, y). If parent is not a container, it is returned.
public static Rectangle getLocalBounds(Component aComponent)
Returns a rectangle containing aComponent relative to aComponent, i.e., (0, 0, width,
height)

.
public static JRootPane getRootPane(Component c)
Finds the root pane containing c. If no JRootPane is found containing c, null is returned.
public static void invokeAndWait(Runnable obj) throws InterruptedException,
InvocationTargetException
public static void invokeLater(Runnable obj)
These methods take
Runnable arguments and place them on the event queue to be executed
after all pending events have been dispatched. The
invokeLater() method essentially just
pushes this Runnable onto the event queue. The invokeAndWait() method pushes it onto
the queue and blocks until it has been dispatched.
JComponent is an example of a Swing component that uses this technique of delayed
execution. It delays revalidation of any layout components until any other events pending
have been handled by calling invokeLater(). Some events rely on the location of their
source to function properly (like tooltips), and moving the components before the event has
been properly dispatched could cause confusion.
As mentioned earlier, Chapter 28 contains a more detailed discussion of these methods.
public static boolean isDescendingFrom(Component a, Component b)
Returns true if component a descends from b in the component hierarchy.
public static boolean isEventDispatchThread()
Returns
true if the current thread is the event-dispatching thread.
public static boolean isLeftMouseButton(MouseEvent anEvent)
public static boolean isMiddleMouseButton(MouseEvent anEvent)
public static boolean isRightMouseButton(MouseEvent anEvent)
These convenience methods return
true if anEvent was performed with the left, middle, or
right mouse button, respectively.
public static final boolean isRectangleContainingRectangle(Rectangle a, Rectangle b)

Returns
true if rectangle a completely contains rectangle b.
public static String layoutCompoundLabel(FontMetrics fm, String text, Icon icon, int
verticalAlignment, int horizontalAlignment, int verticalTextPosition, int
horizontalTextPosition, Rectangle viewR, Rectangle iconR, Rectangle textR, int
textIconGap)

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×