So far, everything NetBeans has known about our beans has
been determined by low-level reflection—that is, by looking at the methods
of our classes. The java.Beans.Introspector
class gathers information on a bean using reflection, then analyzes and
describes a bean to any tool that wants to know about it. The
introspection process works only if the class follows the JavaBeans naming
conventions for its methods; furthermore, it gives us little control over
exactly what properties and events appear in NetBeans menus. For example,
we’ve seen that NetBeans by default shows all the stuff we inherit from
the base Swing component. We can change that by creating BeanInfo
classes for our beans. A BeanInfo
class provides the JavaBeans
introspector with explicit information about the properties, methods, and
events of a bean; we can even use it to customize the text that appears in
menus in NetBeans (and in other IDEs).
A BeanInfo
class implements the
BeanInfo
interface. That’s a
complicated proposition; in most situations, the introspector’s default
behavior is reasonable. Instead of implementing the BeanInfo
interface, we extend the SimpleBeanInfo
class, which implements all of
BeanInfo
’s methods. We can override
specific methods to provide the information we want; when we don’t
override a method, we’ll get the introspector’s default behavior.
In the next few sections, we’ll develop the DialBeanInfo
class that provides explicit
information about our Dial
bean.
We’ll start out by describing the Dial
’s properties. To do so, we must implement
the getPropertyDescriptors()
method. This
method simply returns an array of PropertyDescriptor
objects—one for
each property we want to publicize.
To create a PropertyDescriptor
,
call its constructor with two arguments: the property’s name and the
class. In the following code, we create descriptors for the Dial
’s value
, minimum
, and maximum
properties. We next call a few methods
of the PropertyDescriptor
class to
provide additional information about each property. If our methods were
bound (generated PropertyChangeEvent
s
when modified), we’d call the setBound()
method of
their PropertyDescriptor
s. Our code
is prepared to catch an IntrospectionException
, which can occur if
something goes wrong while creating the property descriptors, such as
encountering a nonexistent method:
//file: DialBeanInfo.java
package
magicbeans
;
import
java.beans.*
;
public
class
DialBeanInfo
extends
SimpleBeanInfo
{
public
PropertyDescriptor
[]
getPropertyDescriptors
()
{
try
{
PropertyDescriptor
value
=
new
PropertyDescriptor
(
"value"
,
Dial
.
class
);
PropertyDescriptor
minimum
=
new
PropertyDescriptor
(
"minimum"
,
Dial
.
class
);
PropertyDescriptor
maximum
=
new
PropertyDescriptor
(
"maximum"
,
Dial
.
class
);
return
new
PropertyDescriptor
[]
{
value
,
minimum
,
maximum
};
}
catch
(
IntrospectionException
e
)
{
return
null
;
}
}
}
Perhaps the most useful thing about DialBeanInfo
is that by providing explicit
information for our properties, we automatically hide other properties
that introspection might find. After compiling DialBeanInfo
and packaging it with the
Dial
, you’ll see that its JComponent
properties no longer appear in the
NetBeans properties editor. (This has been the case all along if you
started with the precompiled example JAR.)
A PropertyDescriptor
can
provide a lot of other information about a property: the names of the
accessor methods (if you decide not to use the standard naming
convention), information about whether the property is constrained, and
a class to use as a property editor (if the standard property editors
aren’t sufficient).
The Dial
bean defines its own
event: the DialEvent
. We’d like to
tell development tools about this event so that we can build
applications using it. The process for telling the world about our
event is similar to what we did previously: we add a method to the DialBeanInfo
class called
getEventSetDescriptors()
, which returns an
array of EventSetDescriptor
s.
Events are described in terms of their listener interfaces, not
in terms of the event classes themselves, so our getEventSetDescriptors()
method creates a
descriptor for the DialListener
interface. Here’s the code to add to the DialBeanInfo
class:
public
EventSetDescriptor
[]
getEventSetDescriptors
()
{
try
{
EventSetDescriptor
dial
=
new
EventSetDescriptor
(
Dial
.
class
,
"dialAdjusted"
,
DialListener
.
class
,
"dialAdjusted"
);
dial
.
setDisplayName
(
"Dial Adjusted"
);
return
new
EventSetDescriptor
[]
{
dial
};
}
catch
(
IntrospectionException
e
)
{
return
null
;
}
}
In this method, we create an EventSetDescriptor
object: dial
. The constructor for an EventSetDescriptor
takes four arguments: the
class that generates the event, the name of the event (the name that
is displayed, by default, by a development tool), the listener class,
and the name of the method to which the event can be delivered. (Other
constructors let you deal with listener interfaces that have several
methods.) After creating the descriptor, we call the setDisplayName()
method to provide a friendly name to be displayed by development tools
such as NetBeans. (This overrides the default name specified in the
constructor.)
Just as the property descriptors we supply hide the properties
that were discovered by reflection, the EventSetDescriptor
s can hide the other
events that are inherited from the base component classes. In theory,
when you recompile DialBeanInfo
,
package it in a JAR, and load it into NetBeans, you should see only
the two events that we have explicitly described: our own DialEvent
and PropertyChangeEvent
(displayed as “Dial
Adjusted” and “Bound property change”). Unfortunately, the current
version of NetBeans ignores this information.
Once we have an EventSetDescriptor
, we can provide other
kinds of information about the event. For example, we can state that
the event is unicast, which means that it can
have only one listener.
Some of the beans that come with NetBeans are displayed on the
palette with a cute icon. This makes life more pleasant for everyone.
To supply an icon for the BeanInfo
object we have been developing, we have it implement the getIcon()
method. You
can supply up to four icons, with sizes of 16 × 16 or 32 × 32, in
color or monochrome. Here’s the getIcon()
method for DialBeanInfo
:
public
class
DialBeanInfo
extends
SimpleBeanInfo
{
...
public
java
.
awt
.
Image
getIcon
(
int
iconKind
)
{
if
(
iconKind
==
BeanInfo
.
ICON_COLOR_16x16
)
{
return
loadImage
(
"DialIconColor16.gif"
);
}
else
if
(
iconKind
==
BeanInfo
.
ICON_COLOR_32x32
)
{
return
loadImage
(
"DialIconColor32.gif"
);
}
else
if
(
iconKind
==
BeanInfo
.
ICON_MONO_16x16
)
{
return
loadImage
(
"DialIconMono16.gif"
);
}
else
if
(
iconKind
==
BeanInfo
.
ICON_MONO_32x32
)
{
return
loadImage
(
"DialIconMono32.gif"
);
}
return
null
;
}
This method is called with a constant, indicating what kind of
icon is being requested; for example, BeanInfo.ICON_COLOR_16x16
requests a 16 × 16
color image. If an appropriate icon is available, it loads the image
and returns an Image
object. If the
icon isn’t available, it returns null
. For convenience, you can package the
images in the same JAR file as the bean and its BeanInfo
class.
Though we haven’t used them here, you can also use a BeanInfo
object to provide information about
other public methods of your bean (for example, array-valued
properties) and other features.
JavaBeans lets you provide a customizer for
your beans. Customizers are objects that do advanced customization for
a bean as a whole; they let you provide your own GUI for tweaking your
bean. We won’t show you how to write a customizer; it’s not too
difficult, but it’s beyond the scope of this chapter. Suffice it to
say that a customizer must implement the java.beans.Customizer
interface and should extend Component
(or JComponent
) so that it can be
displayed.
Property editors are a way of giving the properties sheet
additional capabilities. For example, you could supply a property
editor to let you edit a property type that is specific to your bean.
You could provide a property editor that would let you edit an
object’s price in dollars and cents. We’ve already seen a couple of
property editors: the editor used for Color
-valued properties is fundamentally the
same as a property editor you might write yourself. In addition, the
Molecule
bean uses a property
editor to specify its moleculeName
property. A property editor isn’t quite as fancy
as a customizer, but describing it fully is also beyond the scope of
this chapter.
Again, describing how to write a property editor is also beyond
the scope of this chapter. However, it might help you to know that a
property editor must implement the PropertyEditor
interface; it usually does so
by extending the PropertyEditorSupport
class, which provides
default implementations for most of the methods.
Get Learning Java, 4th Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.