Events can be fired from the server-side Java API of a component, replicating events that occurred on the client. To do this, the first requirement is that the client-side implementation of your component be able to fire an event, as discussed in the client side Firing Events section. The component will then need to have listener registration and event firing methods added to it, and the peer will need to be updated to forward incoming events to the client.
In the example used in this discussion, we'll add a capability to the server-side SpinButton
to fire ActionEvent
s when the ENTER key is pressed on the client.
The Component
base class provides a generic event registration facility in the form of an EventListenerList
object. The getListenerList()
method is used to retrieve (and lazy-create, if necessary) this object.
The modification to a component for event firing support will be to add event listener management methods:
public static final String ACTION_LISTENERS_CHANGED_PROPERTY = "actionListeners";
public void addActionListener(ActionListener l) { getEventListenerList().addListener(ActionListener.class, l); firePropertyChange(ACTION_LISTENERS_CHANGED_PROPERTY, null, l); } public void removeActionListener(ActionListener l) { getEventListenerList().removeListener(ActionListener.class, l); firePropertyChange(ACTION_LISTENERS_CHANGED_PROPERTY, l, null); }
The listener add and remove methods will also fire a property change event to indicate that the component has been updated. This is necessary to inform the synchronization system that the component has changed state.
Additionally we'll add a hasActionListeners()
method to determine if any ActionListener
s are present (this will be needed by the synchronization peer):
public boolean hasActionListeners() { return hasEventListenerList() && getEventListenerList().getListenerCount(ActionListener.class) > 0; }
We'll also add the capability to set an actionCommand
property on the SpinButton
, given that ActionEvent
supports it, and it can be useful in development. To do this, we'll need to add a property name constant for change listeners, an instance variable, and a getter/setter pair:
public static final String ACTION_COMMAND_CHANGED_PROPERTY = "actionCommand";
private String actionCommand;
public String getActionCommand() { return actionCommand; } public void setActionCommand(String newValue) { String oldValue = actionCommand; actionCommand = newValue; firePropertyChange(ACTION_COMMAND_CHANGED_PROPERTY, oldValue, newValue); }
And to provide notification when the ENTER key is pressed, we'll add a fireAction()
method that will create an ActionEvent
and send it to any registered listeners:
private void fireAction() { EventListener[] actionListeners = getEventListenerList().getListeners(ActionListener.class); ActionEvent e = new ActionEvent(this, getActionCommand()); for (int i = 0; i < actionListeners.length; ++i) { ((ActionListener) actionListeners[i]).actionPerformed(e); } }
The final step is to update the processInput()
method of the component, such that when the peer notifies the component of an action, the component can fire an ActionEvent
. To do this, we'll create a constant name for an incoming action event, INPUT_ACTION
, and fire ActionEvent
s when it is received by processInput()
:
public static final String INPUT_ACTION = "action";
public void processInput(String inputName, Object inputValue) { super.processInput(inputName, inputValue); if (VALUE_CHANGED_PROPERTY.equals(inputName)) { Integer value = (Integer) inputValue; setValue(value == null ? 0 : value.intValue()); } else if (INPUT_ACTION.equals(inputName)) { fireAction(); } }
(Changes to the processInput()
method are shown in bold).
The necessary changes to the SpinButtonPeer
class are less significant. We only need to add a new event type in the constructor:
public SpinButtonPeer() { super(); addOutputProperty(SpinButton.VALUE_CHANGED_PROPERTY); addEvent(new EventPeer(SpinButton.INPUT_ACTION, SpinButton.ACTION_LISTENERS_CHANGED_PROPERTY) { public boolean hasListeners(Context context, Component c) { return ((SpinButton) c).hasActionListeners(); } }); }
(Changes to the constructor are shown in bold.)
The addEvent()
method provides an EventPeer
that describes how the event should be handled. The first parameter to the EventPeer
constructor is the property name that should be sent to Component.processInput()
when the event is fired. The second parameter is the property name that will be fired from the Component
in a PropertyChangeEvent
in the case where listeners are added or removed. Additionally, note that the hasListeners()
implementation of EventPeer
is overridden to query SpinButton
's hasActionListeners()
method.