26 June 2009

Base class for ChangeEvent/ChangeListener mangament

If you have written a data model you often want to be able to notify others about the change in the model by using some kind of listener. The Java swing ChangeListener interface is one choice of listener to choose. But the management of a listener list (adding, removing and notification of listeners) is a bit of a hazel to write code for each time you have a model. I searched the Java API but could not find any base class of helper class to solve this. The closest I got was the EventListenerList class. By using this class I've written my own base/helper class:
import javax.swing.event.*;

/**
* Convenience class to notify others of changes
* Can be used as a base class or as an aggregate/helper
*/
public class ChangeEventDispatcher {

protected EventListenerList listenerList = new EventListenerList();
protected ChangeEvent changeEvent = null;

public void addChangeListener(ChangeListener l) {
listenerList.add(ChangeListener.class, l);
}

public void removeFooListener(ChangeListener l) {
listenerList.remove(ChangeListener.class, l);
}

/**
* Called by by child/wrapper class to dispatch a Change event
*
* Code is copied from EventListenerList documentation and adapted to ChangeEvent
*/
public void fireChangeEvent() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==ChangeListener.class) {
// Lazily create the event:
if (changeEvent == null)
changeEvent = new ChangeEvent(this);
((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
}
}
}
}

An example of how to use this class:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

@SuppressWarnings("serial")
public class ChangeTester extends JFrame {

private JButton increaseButton = new JButton("Increase");
private JLabel valueLabel = new JLabel();

private ValueModel model = new ValueModel();

public ChangeTester() {
setLayout(new FlowLayout());
add(increaseButton);
add(valueLabel);

//Change the model when pressing the button
increaseButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
model.increaseValue();
}
});

//The label should be updated when the model is changed
model.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
updateLabelValue();
}
});

updateLabelValue();
pack();
}

private void updateLabelValue() {
valueLabel.setText(Integer.toString(model.getValue()));
}

public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new ChangeTester();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}

/**
* Simple model that extends the ChangeEventDispatcher
* to get listener list handling
*/
public static class ValueModel extends ChangeEventDispatcher {
private int value;

public void increaseValue() {
this.value++;
fireChangeEvent(); //Notify listeners
}

public int getValue() {
return value;
}
}
}

No comments:

Post a Comment