Customizers


A property editor, no matter how sophisticated, is responsible for allowing the user to set one property at a time. Especially if certain properties of a bean relate to each other, it may be more user friendly to give users a way to edit multiple properties at the same time. To enable this feature, you supply a customizer instead of (or in addition to) multiple property editors.

In the example program for this section, we develop a customizer for the chart bean. The customizer lets you set several properties of the chart bean at once, and it lets you specify a file from which to read the data points for the chart. Figure 8-14 shows you one pane of the customizer for the chart bean.

Figure 8-14. The customizer for the ChartBean


To add a customizer to your bean, you must supply a BeanInfo class and override the getBeanDescriptor method, as shown in the following example.

 public ChartBean2BeanInfo extends SimpleBeanInfo {    public BeanDescriptor getBeanDescriptor()    {       return new BeanDescriptor(ChartBean2.class, ChartBean2Customizer.class);    }    . . . } 

Note that you need not follow any naming pattern for the customizer class. The builder can locate it by

  1. Finding the associated BeanInfo class;

  2. Invoking its getBeanDescriptor method; and

  3. Calling the getCustomizerClass method.

(Nevertheless, it is customary to name the customizer as BeanNameCustomizer.)

Example 8-10 has the code for the ChartBean2BeanInfo class that references the ChartBean2Customizer. You see in the next section how that customizer is implemented.

Example 8-10. ChartBean2BeanInfo.java
  1. package com.horstmann.corejava;  2.  3. import java.beans.*;  4.  5. /**  6.    The bean info for the chart bean, specifying the property editors.  7. */  8. public class ChartBeanBeanInfo extends SimpleBeanInfo  9. { 10.    public PropertyDescriptor[] getPropertyDescriptors() 11.    { 12.       try 13.       { 14.          PropertyDescriptor titlePositionDescriptor 15.             = new PropertyDescriptor("titlePosition", ChartBean.class); 16.          titlePositionDescriptor.setPropertyEditorClass(TitlePositionEditor.class); 17.          PropertyDescriptor inverseDescriptor 18.             = new PropertyDescriptor("inverse", ChartBean.class); 19.          inverseDescriptor.setPropertyEditorClass(InverseEditor.class); 20.          PropertyDescriptor valuesDescriptor 21.             = new PropertyDescriptor("values", ChartBean.class); 22.          valuesDescriptor.setPropertyEditorClass(DoubleArrayEditor.class); 23. 24.          return new PropertyDescriptor[] 25.          { 26.             new PropertyDescriptor("title", ChartBean.class), 27.             titlePositionDescriptor, 28.             valuesDescriptor, 29.             new PropertyDescriptor("graphColor", ChartBean.class), 30.             inverseDescriptor 31.          }; 32.       } 33.       catch (IntrospectionException e) 34.       { 35.          e.printStackTrace(); 36.          return null; 37.       } 38.    } 39. } 


 java.beans.BeanInfo 1.1 

  • BeanDescriptor getBeanDescriptor()

    returns a BeanDescriptor object that describes features of the bean.


 java.beans.BeanDescriptor 1.1 

  • BeanDescriptor(Class beanClass, Class customizerClass)

    constructs a BeanDescriptor object for a bean that has a customizer.

    Parameters:

    beanClass

    The Class object for the bean

     

    customizerClass

    The Class object for the bean's customizer


  • Class getBeanClass()

    returns the Class object that defines the bean.

  • Class getCustomizerClass()

    returns the Class object that defines the bean's customizer.

Writing a Customizer Class

Any customizer class you write must implement the Customizer interface. The interface has only three methods:

  • The setObject method, which takes a parameter that specifies the bean being customized

  • The addPropertyChangeListener and removePropertyChangeListener methods, which manage the collection of listeners that are notified when a property is changed in the customizer

It is a good idea to update the visual appearance of the target bean by broadcasting a PropertyChangeEvent whenever the user changes any of the property values, not just when the user is at the end of the customization process.

Unlike property editors, customizers are not automatically displayed. In NetBeans, you must right-click on the bean and select the Customize menu option to pop up the customizer. At that point, the builder calls the setObject method of the customizer that takes the bean being customized as a parameter. Notice that your customizer is thus created before it is actually linked to an instance of your bean. Therefore, you cannot assume any information about the state of a bean in the customizer, and you must provide a default constructor, that is, one without arguments.

Writing a customizer class is done in three parts:

  1. Building the visual interface

  2. Initializing the customizer in the setObject method

  3. Updating the bean by firing property change events when the user changes properties in the interface

By definition, a customizer class is visual. It must, therefore, extend Component or a subclass of Component, such as JPanel. Because customizers typically present the user with many options, it is often handy to use the tabbed pane user interface. We use this approach and have the customizer extend the JTabbedPane class.

The customizer gathers the following information in three panes:

  • Graph color and inverse mode

  • Title and title position

  • Data points

Of course, developing this kind of user interface can be tedious to codeour example devotes over 100 lines just to set it up in the constructor. However, this task requires only the usual Swing programming skills, and we don't dwell on the details here.

One trick is worth keeping in mind. You often need to edit property values in a customizer. Rather than implementing a new interface for setting the property value of a particular class, you can simply locate an existing property editor and add it to your user interface! For example, in our ChartBean2 customizer, we need to set the graph color. Since we know that the BeanBox has a perfectly good property editor for colors, we locate it as follows:

 PropertyEditor colorEditor = PropertyEditorManager.findEditor(Color.Class); 

We then call getCustomEditor to get the component that contains the user interface for setting the colors.

 Component colorEditorComponent = colorEditor.getCustomEditor(); // now add this component to the UI 

Once we have all components laid out, we initialize their values in the setObject method. The setObject method is called when the customizer is displayed. Its parameter is the bean that is being customized. To proceed, we store that bean referencewe'll need it later to notify the bean of property changes. Then, we initialize each user interface component. Here is a part of the setObject method of the chart bean customizer that does this initialization.

 public void setObject(Object obj) {    bean = (ChartBean2) obj;    titleField.setText(bean.getTitle());    colorEditor.setValue(bean.getGraphColor());    . . . } 

Finally, we hook up event handlers to track the user's activities. Whenever the user changes the value of a component, the component fires an event that our customizer must handle. The event handler must update the value of the property in the bean and must also fire a PropertyChangeEvent so that other listeners (such as the property inspector) can be updated. Let us follow that process with a couple of user interface elements in the chart bean customizer.

When the user types a new title, we want to update the title property. We attach a DocumentListener to the text field into which the user types the title.

 titleField.getDocument().addDocumentListener(new    DocumentListener()    {       public void changedUpdate(DocumentEvent event)       {          setTitle(titleField.getText());       }       public void insertUpdate(DocumentEvent event)       {          setTitle(titleField.getText());       }       public void removeUpdate(DocumentEvent event)       {          setTitle(titleField.getText());       }    }); 

The three listener methods call the setTitle method of the customizer. That method calls the bean to update the property value and then fires a property change event. (This update is necessary only for properties that are not bound.) Here is the code for the setTitle method.

 public void setTitle(String newValue) {    if (bean == null) return;    String oldValue = bean.getTitle();    bean.setTitle(newValue);    firePropertyChange("title", oldValue, newValue); } 

When the color value changes in the color property editor, we want to update the graph color of the bean. We track the color changes by attaching a listener to the property editor. Perhaps confusingly, that editor also sends out property change events.

 colorEditor.addPropertyChangeListener(new    PropertyChangeListener()    {       public void propertyChange(PropertyChangeEvent event)       {          setGraphColor((Color) colorEditor.getValue());       }    }); 

Whenever the color value of the color property editor changes, we call the setGraphColor method of the customizer. That method updates the graphColor property of the bean and fires a different property change event that is associated with the graphColor property.

 public void setGraphColor(Color newValue) {    if (bean == null) return;    Color oldValue = bean.getGraphColor();    bean.setGraphColor(newValue);    firePropertyChange("graphColor", oldValue, newValue); } 

Example 8-11 provides the full code of the chart bean customizer.

This particular customizer just sets properties of the bean. In general, customizers can call any methods of the bean, whether or not they are property setters. That is, customizers are more general than property editors. (Some beans may have features that are not exposed as properties and that can be edited only through the customizer.)

Example 8-11. ChartBean2Customizer.java

[View full width]

   1. package com.horstmann.corejava;   2.   3. import java.awt.*;   4. import java.awt.event.*;   5. import java.beans.*;   6. import java.io.*;   7. import java.text.*;   8. import java.util.*;   9. import javax.swing.*;  10. import javax.swing.event.*;  11.  12. /**  13.    A customizer for the chart bean that allows the user to  14.    edit all chart properties in a single tabbed dialog.  15. */  16. public class ChartBean2Customizer extends JTabbedPane  17.    implements Customizer  18. {  19.    public ChartBean2Customizer()  20.    {  21.       data = new JTextArea();  22.       JPanel dataPane = new JPanel();  23.       dataPane.setLayout(new BorderLayout());  24.       dataPane.add(new JScrollPane(data), BorderLayout.CENTER);  25.       JButton dataButton = new JButton("Set data");  26.       dataButton.addActionListener(new  27.          ActionListener()  28.          {  29.             public void actionPerformed(ActionEvent event) { setData(data.getText()); }  30.          });  31.       JPanel p = new JPanel();  32.       p.add(dataButton);  33.       dataPane.add(p, BorderLayout.SOUTH);  34.  35.       JPanel colorPane = new JPanel();  36.       colorPane.setLayout(new BorderLayout());  37.  38.       normal = new JCheckBox("Normal", true);  39.       inverse = new JCheckBox("Inverse", false);  40.       p = new JPanel();  41.       p.add(normal);  42.       p.add(inverse);  43.       ButtonGroup g = new ButtonGroup();  44.       g.add(normal);  45.       g.add(inverse);  46.       normal.addActionListener(new  47.          ActionListener()  48.          {  49.             public void actionPerformed(ActionEvent event) { setInverse(false); }  50.          });  51.  52.       inverse.addActionListener(  53.          new ActionListener()  54.          {  55.             public void actionPerformed(ActionEvent event) { setInverse(true); }  56.          });  57.  58.       colorEditor = PropertyEditorManager.findEditor(Color.class);  59.       colorEditor.addPropertyChangeListener(  60.          new PropertyChangeListener()  61.          {  62.             public void propertyChange(PropertyChangeEvent event)  63.             {  64.                setGraphColor((Color) colorEditor.getValue());  65.             }  66.          });  67.  68.       colorPane.add(p, BorderLayout.NORTH);  69.       colorPane.add(colorEditor.getCustomEditor(), BorderLayout.CENTER);  70.  71.       JPanel titlePane = new JPanel();  72.       titlePane.setLayout(new BorderLayout());  73.  74.       g = new ButtonGroup();  75.       position = new JCheckBox[3];  76.       position[0] = new JCheckBox("Left", false);  77.       position[1] = new JCheckBox("Center", true);  78.       position[2] = new JCheckBox("Right", false);  79.  80.       p = new JPanel();  81.       for (int i = 0; i < position.length; i++)  82.       {  83.          final int value = i;  84.          p.add(position[i]);  85.          g.add(position[i]);  86.          position[i].addActionListener(new  87.             ActionListener()  88.             {  89.                public void actionPerformed(ActionEvent event) { setTitlePosition (value); }  90.             });  91.       }  92.  93.       titleField = new JTextField();  94.       titleField.getDocument().addDocumentListener(  95.          new DocumentListener()  96.          {  97.             public void changedUpdate(DocumentEvent evt) {  setTitle(titleField .getText()); }  98.             public void insertUpdate(DocumentEvent evt) { setTitle(titleField.getText ()); }  99.             public void removeUpdate(DocumentEvent evt) { setTitle(titleField.getText ()); } 100.          }); 101. 102.       titlePane.add(titleField, BorderLayout.NORTH); 103.       titlePane.add(p, BorderLayout.SOUTH); 104.       addTab("Color", colorPane); 105.       addTab("Title", titlePane); 106.       addTab("Data", dataPane); 107.    } 108. 109.    /** 110.       Sets the data to be shown in the chart. 111.       @param s a string containing the numbers to be displayed, 112.       separated by white space 113.    */ 114.    public void setData(String s) 115.    { 116.       StringTokenizer tokenizer = new StringTokenizer(s); 117. 118.       int i = 0; 119.       double[] values = new double[tokenizer.countTokens()]; 120.       while (tokenizer.hasMoreTokens()) 121.       { 122.          String token = tokenizer.nextToken(); 123.          try 124.          { 125.             values[i] = Double.parseDouble(token); 126.             i++; 127.          } 128.          catch (NumberFormatException e) 129.          { 130.          } 131.       } 132.       setValues(values); 133.    } 134. 135.    /** 136.       Sets the title of the chart. 137.       @param newValue the new title 138.    */ 139.    public void setTitle(String newValue) 140.    { 141.       if (bean == null) return; 142.       String oldValue = bean.getTitle(); 143.       bean.setTitle(newValue); 144.       firePropertyChange("title", oldValue, newValue); 145.    } 146. 147.    /** 148.       Sets the title position of the chart. 149.       @param i the new title position (ChartBean2.LEFT, 150.       ChartBean2.CENTER, or ChartBean2.RIGHT) 151.    */ 152.    public void setTitlePosition(int i) 153.    { 154.       if (bean == null) return; 155.       Integer oldValue = new Integer(bean.getTitlePosition()); 156.       Integer newValue = new Integer(i); 157.       bean.setTitlePosition(i); 158.       firePropertyChange("titlePosition", oldValue, newValue); 159.    } 160. 161.    /** 162.       Sets the inverse setting of the chart. 163.       @param b true if graph and background color are inverted 164.    */ 165.    public void setInverse(boolean b) 166.    { 167.       if (bean == null) return; 168.       Boolean oldValue = new Boolean(bean.isInverse()); 169.       Boolean newValue = new Boolean(b); 170.       bean.setInverse(b); 171.       firePropertyChange("inverse", oldValue, newValue); 172.    } 173. 174.    /** 175.       Sets the values to be shown in the chart. 176.       @param newValue the new value array 177.    */ 178.    public void setValues(double[] newValue) 179.    { 180.       if (bean == null) return; 181.       double[] oldValue = bean.getValues(); 182.       bean.setValues(newValue); 183.       firePropertyChange("values", oldValue, newValue); 184.    } 185. 186.    /** 187.       Sets the color of the chart 188.       @param newValue the new color 189.    */ 190.    public void setGraphColor(Color newValue) 191.    { 192.       if (bean == null) return; 193.       Color oldValue = bean.getGraphColor(); 194.       bean.setGraphColor(newValue); 195.       firePropertyChange("graphColor", oldValue, newValue); 196.    } 197. 198.    public void setObject(Object obj) 199.    { 200.       bean = (ChartBean2) obj; 201. 202.       data.setText(""); 203.       for (double value : bean.getValues()) 204.          data.append(value + "\n"); 205. 206.       normal.setSelected(!bean.isInverse()); 207.       inverse.setSelected(bean.isInverse()); 208. 209.       titleField.setText(bean.getTitle()); 210. 211.       for (int i = 0; i < position.length; i++) 212.          position[i].setSelected(i == bean.getTitlePosition()); 213. 214.       colorEditor.setValue(bean.getGraphColor()); 215.    } 216. 217.    public Dimension getPreferredSize() { return new Dimension(XPREFSIZE, YPREFSIZE); } 218. 219.    private static final int XPREFSIZE = 200; 220.    private static final int YPREFSIZE = 120; 221.    private ChartBean2 bean; 222.    private PropertyEditor colorEditor; 223. 224.    private JTextArea data; 225.    private JCheckBox normal; 226.    private JCheckBox inverse; 227.    private JCheckBox[] position; 228.    private JTextField titleField; 229. } 


 java.beans.Customizer 1.1 

  • void setObject(Object bean)

    specifies the bean to customize.



    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net