A bean may want to provide some way for the user of a
beanbox program to customize its properties other than by setting
them one at a time. A bean can do this by
creating a Customizer class for itself, and
registering the customizer class with the
BeanDescriptor object returned by its
BeanInfo class, as we saw in
Example 10.5.
A customizer must be some kind of AWT component that is
suitable for display in a dialog box created by the beanbox.
Therefore, a customizer class is typically a subclass of
Panel. In addition, a customizer must implement the
Customizer interface. This interface consists of
methods for adding and removing property change event
listeners and a setObject() method that the beanbox
calls to tell the customizer what bean object it is
customizing. Whenever the user makes a change to the bean
through the customizer, the customizer should send a
PropertyChangeEvent to any interested listeners.
Finally, like a property editor, a customizer must have a
no-argument constructor, so it can easily be instantiated
by a beanbox.
Example 10.8
shows a customizer for our YesNoDialog bean. This
customizer displays a panel that has the same layout as a
YesNoDialog, but it substitutes a
TextArea
object for the message display and three
TextField objects for the three buttons that the
dialog can display. These text entry areas allow the user
to enter values for the message, yesLabel,
noLabel, and cancelLabel
properties.
Figure 10.3
shows this customizer panel displayed within a dialog box
created by the beanbox program. Again, note that the
Done button is part of the beanbox dialog, not
part of the customizer itself.
package oreilly.beans.yesno;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
/**
* This class is a customizer for the YesNoDialog bean. It displays a
* TextArea and three TextFields where the user can enter the dialog message
* and the labels for each of the three buttons. It does not allow the
* dialog title or other resources to be set.
*/
public class YesNoDialogCustomizer extends Panel
implements Customizer, TextListener
{
protected YesNoDialog bean; // The bean being customized.
protected TextComponent message, fields[]; // Components used by customizer
// Default constructor: YesNoDialogCustomizer() { super(); }
// The bean box calls this method to tell us what object to customize.
// This method will always be called before the customizer is displayed,
// so it is safe to create the customizer GUI here.
public void setObject(Object o) {
bean = (YesNoDialog)o; // Save the object we're customizing.
// Put a label at the top of the panel.
this.setLayout(new BorderLayout());
this.add(new Label("Enter the message to appear in the dialog:"), "North");
// And a big text area below it for entering the dialog message.
message = new TextArea(bean.getMessage());
message.addTextListener(this);
// TextAreas don't know how big they want to be. You must tell them.
message.setSize(400, 200);
this.add(message, "Center");
// Then add a row of textfields for entering the button labels.
Panel buttonbox = new Panel(); // The row container.
buttonbox.setLayout(new GridLayout(1, 0, 25, 10)); // Equally spaced items.
this.add(buttonbox, "South"); // Put row on bottom.
// Now go create three TextFields to put in this row. But actually
// position a Label above each, so create a container for each
// TextField+Label combination.
fields = new TextComponent[3]; // Array of TextFields.
String[] labels = new String[] { // Labels for each.
"Yes Button Label", "No Button Label", "Cancel Button Label"};
String[] values = new String[] { // Initial values of each.
bean.getYesLabel(), bean.getNoLabel(), bean.getCancelLabel()};
for(int i = 0; i < 3; i++) {
Panel p = new Panel(); // Create a container.
p.setLayout(new BorderLayout()); // Give it a BorderLayout.
p.add(new Label(labels[i]), "North"); // Put a label on the top.
fields[i] = new TextField(values[i]); // Create the text field.
p.add(fields[i], "Center"); // Put it below the label.
fields[i].addTextListener(this); // Set the event listener.
buttonbox.add(p); // Add container to row.
}
}
// Add some space around the outside of the panel.
public Insets getInsets() { return new Insets(10, 10, 10, 10); }
// This is the method defined by the TextListener interface. Whenever the
// user types a character in the TextArea or TextFields, this will get
// called. It updates the appropriate property of the bean and fires a
// property changed event, as all customizers are required to do.
// Note that we are not required to fire an event for every keystroke.
// Instead we could include an "Apply" button that would make all the
// changes at once, with a single property changed event.
public void textValueChanged(TextEvent e) {
TextComponent t = (TextComponent)e.getSource();
String s = t.getText();
if (t == message) bean.setMessage(s);
else if (t == fields[0]) bean.setYesLabel(s);
else if (t == fields[1]) bean.setNoLabel(s);
else if (t == fields[2]) bean.setCancelLabel(s);
listeners.firePropertyChange(null, null, null);
}
// This code uses the PropertyChangeSupport class to maintain a list of
// listeners interested in the edits we make to the bean.
protected PropertyChangeSupport listeners = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener l) {
listeners.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
listeners.removePropertyChangeListener(l);
}
}
|