| |
When the value of a Bean property changes, it's sometimes
useful for interested Bean listeners to be informed of those
changes.
Bound properties tell these interested listeners
when their value changes. They cause events to be sent to
those interested listeners when a value changes, to allow
those listeners to take some appropriate action in response.
For example, a SpreadSheet Bean might tell a PieChart bean to
redraw itself whenever the spreadsheet changes.
Bound Property Requirements
To implement a bound property, you must follow the normal
property naming conventions, and also implement the
following:
- The Bean must implement the following two methods to
allow other classes to register as PropertyChangeListeners:
public void addPropertyChangeListener(
PropertyChangeListener listener) public void removePropertyChangeListener(
PropertyChangeListener listener)
- Whenever the value of the bound property changes, the
Bean must send a PropertyChangeEvent
to all registered listeners
The PropertyChangeListener
Interface
The PropertyChangeListener
interface has a single method:
public void propertyChange(PropertyChangeEvent evt)
When a class implements this interface, it must implement
this method. The method is called, if the class is registered
as a PropertyChangeListener,
when it receives a PropertyChangeEvent.
The PropertyChangeSupport
Class
The java.beans package contains a convenience class, PropertyChangeSupport,
that makes it trivial to write the addPropertyChangeListener
and removePropertyChangeListener
methods:
private void PropertyChangeSupport m_changeSupport =
new PropertyChangeSupport(this); public void addPropertyChangeListener(PropertyChangeListener l)
{
m_changeSupport.addPropertyChangeListener(l);
} public void removePropertyChangeListener(PropertyChangeListener l)
{
m_changeSupport.removePropertyChangeListener(l);
}
PropertyChangeSupport
also makes it simple to "fire" a PropertyChangeEvent:
m_changeSupport.firePropertyChange(propertyName,
oldValue,
newValue);
If the property has a primitive type, then you must
"wrap" the values:
m_changeSupport.firePropertyChange(propertyName,
new Integer(oldValue),
new Integer(newValue));
Example: BarChart,
InBox,
and ColorBar
Beans
The example is an extension of the earlier BarChart Bean.
Here's what it looks like as an applet:
|
You'll note that it looks somewhat
similar to the previous version. It has the BarChart
Bean, as before (although it's a modified version),
but in place of the two buttons, it now has an input
text field to supply the value for the bar chart.
The input text field is an InBox
Bean, which will only allow you to enter certain
characters -- digits, Enter, Delete, Left & Right
Arrows, etc. All others will be ignored. This means
that you can't type in an invalid integer.
Whenever the value is changed (enter a new value,
and hit Enter, or Tab), it will cause the bar chart
to display appropriately.
This is implemented using a bound property in the InBox
Bean, and a PropertyChangeListener
in the BarChart
Bean.
|
Here's the code for the modified BarChart
Bean:
package javaBeans;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
/**
* A BarChart JavaBean class.
*
* @author Bryan J. Higgs, 12 March, 2000
*/
public class BarChart extends Canvas
implements PropertyChangeListener
{
/**
* Constructs a BarChart instance
*/
public BarChart()
{
setSize(m_barWidth, m_barHeight);
}
//// Accessor methods ////
public void setFloodColor(Color floodColor)
{
m_floodColor = floodColor;
}
public Color getFloodColor()
{
return m_floodColor;
}
public void setFillColor(Color fillColor)
{
m_fillColor = fillColor;
}
public Color getFillColor()
{
return m_fillColor;
}
public void setBorderColor(Color borderColor)
{
m_borderColor = borderColor;
}
public Color getBorderColor()
{
return m_borderColor;
}
public void setBorderWidth(int borderWidth)
{
m_borderWidth = borderWidth;
}
public int getBorderWidth()
{
return m_borderWidth;
}
public void setBarWidth(int barWidth)
{
m_barWidth = barWidth;
}
public int getBarWidth()
{
return m_barWidth;
}
public void setBarHeight(int barHeight)
{
m_barHeight = barHeight;
}
public int getBarHeight()
{
return m_barHeight;
}
/**
* Sets the percent for bar display.
*/
public void setPercent(int percent)
{
if (percent <= 100 && percent >= 0)
{
m_percent = percent;
repaint();
}
}
/**
* Gets the percent for bar display.
*/
public int getPercent()
{
return m_percent;
}
/**
* Allows the user to set the bar size visually.
*/
public void setBounds(int x, int y, int width, int height)
{
super.setBounds(x, y, width, height);
setBarWidth(width);
setBarHeight(height);
}
// Respond to a property change event for the property
// "value" by updating my value to a new value.
public void propertyChange(PropertyChangeEvent event)
{
Integer newValue = (Integer)event.getNewValue();
setPercent(newValue.intValue());
}
/**
* Paints the BarChart using the specified Graphics.
*/
public void paint(Graphics g)
{
// Use fill color to fill entire component
g.setColor(m_fillColor);
g.fillRect(0, 0, m_barWidth, m_barHeight);
int f = (int)(0.5 + (m_percent/100.0) *
(m_barHeight - (2 * m_borderWidth)));
g.setColor(m_floodColor);
g.fillRect(0, m_barHeight - m_borderWidth - f,
m_barWidth, f);
// Use border color for border, if applicable
if (m_borderWidth > 0)
{
g.setColor(m_borderColor);
g.fillRect(0, 0, m_borderWidth, m_barHeight);
g.fillRect(0, 0, m_barWidth, m_borderWidth);
g.fillRect(0, m_barHeight - m_borderWidth,
m_barWidth, m_borderWidth);
g.fillRect(m_barWidth - m_borderWidth, 0,
m_borderWidth, m_barHeight);
}
}
//// Private data ////
private int m_percent = 0;
private Color m_floodColor = Color.blue;
private Color m_fillColor = Color.red;
private Color m_borderColor = Color.black;
private int m_barWidth = 25;
private int m_barHeight = 150;
private int m_borderWidth = 2;
} |
Here is the code for the InBox
Bean:
package javaBeans;
import java.awt.TextField;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
/**
* An IntBox Bean is a TextField that only accepts positive integers.
*
* @author Bryan J. Higgs, 8 April, 2000
*/
public class IntBox extends TextField
{
/**
* Default constructor.
*/
public IntBox()
{
// Must enable key events on this component
enableEvents(java.awt.AWTEvent.KEY_EVENT_MASK);
}
/**
* Allow other objects to register interest in my bound properties
*/
public void addPropertyChangeListener(PropertyChangeListener listener)
{
m_pcs.addPropertyChangeListener(listener);
}
/**
* Allow other objects to remove their interest in my bound properties
*/
public void removePropertyChangeListener(PropertyChangeListener listener)
{
m_pcs.removePropertyChangeListener(listener);
}
/**
* Return the integer value of this object.
* (It's zero if we can't parse it.)
*/
public int getValue()
{
try
{
int i = Integer.parseInt(getText());
return i;
}
catch (NumberFormatException ex) {
return 0;
}
}
/**
* Set new value
*/
public void setValue(int value)
{
int oldValue = m_prevValue;
setText(Integer.toString(value));
int newValue = getValue();
// This call sends a property change event to all listeners.
m_pcs.firePropertyChange("value",
new Integer(oldValue),
new Integer(newValue));
m_prevValue = newValue;
}
/**
* Intercept key events so we can discard non-numeric and
* non-special characters. (For example, we don't allow any
* of the alphabetic characters, punctuation, etc.)
*/
public void processKeyEvent(KeyEvent event)
{
int id = event.getID(); // Get the event type
int c = event.getKeyCode(); // and the key code
if (id == KeyEvent.KEY_TYPED) // KEY_TYPED doesn't have a code
c = (int)event.getKeyChar();// So get the character instead
// Only these characters receive normal processing.
if (Character.isDigit((char)c) ||
c == KeyEvent.VK_DELETE ||
c == KeyEvent.VK_BACK_SPACE ||
c == KeyEvent.VK_LEFT ||
c == KeyEvent.VK_RIGHT ||
c == KeyEvent.VK_TAB ||
c == KeyEvent.VK_ENTER
)
{
if (id == KeyEvent.KEY_PRESSED && // Only do it once!
(c == KeyEvent.VK_ENTER || c == KeyEvent.VK_TAB)
)
{
setValue(getValue());
}
// Allow normal processing
super.processKeyEvent(event);
return;
}
// Discard all other characters
event.consume();
}
//// Private Data ////
// The previous value of the IntBox.
private int m_prevValue = 0;
// Delegate property change operations to this object
private PropertyChangeSupport m_pcs =
new PropertyChangeSupport(this);
} |
and finally, the code for the ColorBarApplet
(which is also a Bean):
package javaBeans;
import java.awt.BorderLayout;
import java.awt.Panel;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.applet.Applet;
/**
* The ColorBar Applet Bean contains two beans: IntBox and BarChart.
*
* @author Bryan J. Higgs, 8 April, 2000
*/
public class ColorBarApplet extends Applet
{
/**
* Constructor
*/
public ColorBarApplet()
{
setLayout(new BorderLayout());
// Create and initialize BarChart.
m_barChart = new BarChart();
m_barChart.setPercent(0);
// Create and initialize IntBox
m_intBox = new IntBox();
m_intBox.setText("0");
Panel p = new Panel();
p.add(m_barChart);
add(p, BorderLayout.NORTH);
add(m_intBox, BorderLayout.SOUTH);
// Set the BarChart to listen for IntBox property change events.
m_intBox.addPropertyChangeListener(m_barChart);
// Ensure that when the applet displays, that
// the bar is properly sized.
addComponentListener( new ComponentAdapter()
{
public void componentShown(ComponentEvent ev)
{
Rectangle bounds = getBounds();
setBounds(bounds);
doLayout();
}
}
);
}
/**
* This allows the user to set the size visually.
*/
public void setBounds(int x, int y, int width, int height)
{
super.setBounds(x, y, width, height);
Rectangle intBoxBounds = m_intBox.getBounds();
m_barChart.setBarWidth(width - 20);
m_barChart.setBarHeight(height - intBoxBounds.height - 10);
}
//// Private Data ////
private BarChart m_barChart;
private IntBox m_intBox;
} |
|