You are hereAria User Guide / Section III: In Depth / Event Handling

Event Handling


By luano - Posted on 14 January 2009

Aria aims to simplify application development by reducing the amount of boilerplate code that must be generated for an application. One of the most time consuming activities and a troublesome feature of application development is the wiring together of components and events.

Java's abstract event handling mechanism is certainly flexible but more often than not the code needed for even simple event handling is repetitive and adds little of particular value to the application. In Aria the most common instances of this event handling wiring is taken care of by basic library functions so that all the Aria user need do is specify what is specific to a particular event.

Aria's event handling relies on the basic Java event handling mechanisms and at any point you can go beyond the Aria library and work directly with the low level event mechanisms. This chapter shows how to work with both mechanisms.

Adding EventHandlers interactively

Adding event handling in Aria is very easy. Simply select the component whose event you want to handle within the page designer. Then in the properties palette locate the event of interest. In the screen shot below you can see a mouse event being entered for the selected button.

Once the name has been entered and you press enter Aria finds the event handler in the source code or adds a new event handler if the handler doesn't exist already.

To correctly add the handler Aria must do a few extra things that may require additional input or modify the source code in other ways. Of these tasks the most obvious occurs if no name has been given to the component being edited then a popup dialog will appear requesting that you enter a name for the component.

Once a name is available for the component Aria can proceed and opens the source code in a text editor at the new event handler method.

If necessary a new source file may have been created into which the handler is added. The event is bound to the user interface and the new Java class by a declaration in the page's XML. You can see the event declaration by switching to the page designer and selecting the XML button in the designer's toolbar..

The page XML is updated as soon as the new method is added and the XML text editor is displayed.

And that's it, all of the infrastructure for event handling is taken care of and you can begin entering the application specific business logic.

Basic event handling

Regardless of how you edit the event handlers for your application the basic mechanisms are the same. The page's XML declaration names the event to be used to respond to a particular event type for a given component.

While the XML could point to a method in the page's base class or some existing utility class, most pages involve some form of custom class for handling the custom logic that belongs to the page. Pages can derive directly from Page but by changing the base class you can customize the event handling for components

This example re-introduces some Java code. Now the base class is our own custom written class derived from Page so that calls specified in the XML can be intercepted by the custom class.

A new Java class declaration

  1. public class EventHandling extends Page

The XML file now specifies this custom class as it's base class and defines the two buttons which will have events attached.

A page specifying events

  1. <Page class="org.formaria.samples.xmlbuild.EventHandling">
  2. <Components>
  3. <Component name="btnProceed" type="XBUTTON" x="40" y="10" w="60" h="20" content="Proceed"/>
  4. <Component name="btnCancel" type="XBUTTON" x="110" y="10" w="60" h="20" content="Proceed"/>
  5. </Components>
  6.  
  7. <Events>
  8. <Event method="nextPage" target="btnProceed" type="MouseHandler" next="xmlnavsamplescr2"/>
  9. <Event method="cancel" target="btnCancel" type="MouseHandler"/>
  10. </Events>

So now the java code handling the events is as follows:

The Java class's implementation of the events

  1. public void nextPage()
  2. {
  3. if ( wasMouseClicked() ){
  4. // Do something here...
  5. PageManager.showPage( getAttribute( "next" ));
  6. }
  7. }
  8.  
  9. public void cancel()
  10. {
  11. if ( wasMouseClicked() ){
  12. Button button = (Button)findComponent( "btnCancel" );
  13. button.setText( "Clicked!" );
  14. }
  15. }

Built-in event handlers

Aria supports several basic types event, the support types are described below:

Various event handler types

Type

Usage

ActionHandler

Action handlers respond to ActionEvents such as button clicks, selections, edit completions and so on. This is a simple interface with just a single event.

MouseHandler

Mouse events comprise an number of events including mouse entry, exit and changes of state including when the mouse is pressed, released and clicked. It is normal to check the type of event that trigger the call to a mouse handler method unless you have some generic action for all the above events.

MouseMotionHandler

Like the above mouse handler the mouse motion handler responds to more than one mouse event, namely the mouse moved and mouse dragged events. It is unusual to interact with this type of handler.

ItemHandler

This type of handler is invoked whenever the state of a component changes, for example the selection state of a radio button or a check box.

KeyHandler

This type of handler is used for handling keyboard event including key pressed, released and typed. Sometimes this type of handler is used to help enforce validation rules on input fields.

FocusHandler

A change of focus will trigger this handler both when a component gains focus and when it looses focus.

MenuHandler

Invoked whenever a menu item is selected.

TextHandler

Invoked whenever a text value has changed.

All of these basic event handlers are added via the Page class or its derivatives. We have already seen an example above of adding a handler via XML but the handlers can also be added via Java with just as much ease. Here's an example that is equivalent to the above XML:

Adding an event handler with Java

  1. addMouseHandler( btnProceed, "nextPage" );
  2. addMouseHandler( btnCancel, "cancel" );

Accessing event objects

In the case of multiple event being processed by a single event handler it is sometimes useful to query the properties of the event that caused the handler to be invoked. This is frequently the case with mouse events and key events (and special helper methods are defined for working with mouse events).

To access the event that triggered the handler it is necessary to call the getCurrentEvent method. The event can then be cast to whatever the appropriate type and the event specific data is then accessible. The example below shows how information is extracted from a mouse event. Note that the event is first checked to ensure that it is a mouse event before attempting to cast the value.

Accessing the current event

  1. public void handleMouseClicks()
  2. {
  3. if ( wasMouseClicked()) {
  4. MouseEvent me = (MouseEvent)getCurrentEvent();
  5. Point pt = me.getPoint();
  6. statusLabel.setText( "The mouse is at: + Integer.toString(pt.x) + "," +
  7. Integer.toString(pt.y));
  8. }
  9. }

Under the hood

In adding event handlers Aria has to go through a number of steps and it is worth understanding these steps, if only for the sake of in-depth knowledge.

Consider first of all the process of adding the event handler. Aria constructs your page and the user interface for the page. This is really a process of adding the user interface components to an instance of your page's class. That class is derived from Page which includes the event handling mechanism. In processing the event declaration in your page's XML Aria therefore uses the page's event handling mechanism.

Like most normal Java code Aria pages respond to event through the standard listener interfaces and to save coding the page implements a number of the more common listener interfaces. The page can therefore listen for the common events associated with these interfaces. One of the most common is the ActionListener interface which is invoked in response to an event such as a button click. So when you ask a Aria page to add an actionHandler for a button it justs adds itself as an ActionListener for the button.

Once the button is clicked the page is notified through the interface. The method you registered is then looked-up and invoked via reflection (since Aria doesn't really have any knowledge of your method). No great magic there, but the process saves some more of the plumbing code.

Custom event handlers

It is possible to add support for just about any event type to a Aria application. The EventAdapter interface is an interface that wraps an object implementing the particular listener of interest. The wrapper is intended to be generic and therefore delegates most of its work to the Aria event handler.

Adding a custom event handler

  1. <Event method="tabChanged"
  2. target="tabPanel"
  3. type="org.formaria.events.swing.ChangeEventHelper"/>

The ChangeEventHelper implements the ChangeListener interface and in responding to ChangeEvents it invokes the named method. The framework assumes that the class referenced is an instance of the EventAdapter interface, which specifies the parameters needed to create and add the event handler.

The complete source for the above helper is shown below

ChangeEventHelper, a custom event handler

  1. package org.formaria.events.swing;
  2.  
  3. import javax.swing.event.ChangeEvent;
  4. import javax.swing.event.ChangeListener;
  5. import org.formaria.aria.events.EventAdapter;
  6. import org.formaria.aria.events.AriaEventHandler;
  7.  
  8. /**
  9.   * A helper for the Swing ChangeListener interface
  10.   * <p>Copyright (c) Formaria Ltd., 2009
  11.   * <p>License: see license.txt
  12.   * $Revision: 1.2 $
  13.   */
  14. public class ChangeEventHelper implements ChangeListener, XEventAdapter
  15. {
  16. protected AriaEventHandler eventHandler;
  17.  
  18. /**
  19.   * Set the current event handler
  20.   * @param xeh the event handler
  21.   */
  22. public void setEventHandler( AriaEventHandler xeh )
  23. {
  24. eventHandler = xeh;
  25. }
  26.  
  27. /**
  28.   * Get the name of the adder method e.g. addActionListener
  29.   * @return the method name
  30.   */
  31. public String getAddedMethodName()
  32. {
  33. return "addChangeListener";
  34. }
  35.  
  36. /**
  37.   * Get the name of the listener e.g. java.awt.event.ActionListener
  38.   * @return the listener name
  39.   */
  40. public String getListenerInterfaceName()
  41. {
  42. return "javax.swing.event.ChangeListener";
  43. }
  44.  
  45. /**
  46.   * Get the event mask
  47.   * @return the mask e.g. AWTEvent.ACTION_MASK
  48.   */
  49. public long getEventMask()
  50. {
  51. return 0x100000;
  52. }
  53.  
  54. /**
  55.   * The change event has occured
  56.   */
  57. public void stateChanged( ChangeEvent e )
  58. {
  59. try {
  60. eventHandler.invoke( getEventMask(), e );
  61. }
  62. catch ( Exception ex ) {
  63. ex.printStackTrace();
  64. }
  65. }
  66. }
  67.  

In the case of the above class the listener specific code is the stateChanged method, but all that does is delegate to the event handler.

Exception handling

Any application logic relying on user input is susceptible to a variety of errors and exceptional conditions that make process impossible to complete. In the Java world it is normal to trap these exceptions as they occur.

In Aria exception handling is closely tied to the event handling mechanism and the validation mechanism. The idea is that exceptions and validation errors can be handled in a manner similar to the event handling mechanism. Furthermore exceptions can be handled with a certain degree of separation from the normal flow of events since, as the name suggests they occur in exceptional circumstances.

For example an incomplete form could potentially produce a series of exceptions during event processing and during validation. From a user point of view it is unlikely that individual warnings would be produced for each error and instead the user would be informed of a set of errors or a synopsis of the errors. Aria supports this type of error handling and also redirection of errors to a central error handler. You can find more information on exception handling and input validation at See Writing a custom exception handler.

Generic event handling

Aria has built in handlers for the event types listed above however in some cases it is not adequate for all demands or it may be necessary to handle some other event types. Therefore the Aria API includes a number of methods to assist in integration of other event types.

To customize the event handling it is necessary to subclass the Page class. In subclassing Page there are three possible mechanisms for changing the event handling.

First the setEventHandler method can be used to set a specific event handler. Changing the event handler gives considerable scope to change the behavior of the event handling within Aria.

Secondly individual events can be added with the methods listed below. Choosing to add or modify specific events means that the changes will have less impact than modifying the event handler but at the cost of more work if the changes are to be reused elsewhere.

Finally the underlying Java event handling mechanisms can be used to handle events just as would be the case in a plain old Java application. Nothing in Aria should interfere with the basic event handling mechanism.

Event Handler API

  1. public void setEventHandler( EventHandler eh );
  2.  
  3. public void addListener( Object comp, String listenerName, String argType );
  4. public void addHandler( Object comp, long eventType, String methodName ) throws Exception;

The first method setEventHandler sets the event handler class for the page. This method should be called in the page constructor. The page delegates to the event handler whenever an attempt is made to add a new event handler.

Most applications call specific methods in the page's API such asaddActionHandler, which in turn delegates to the event handler instance. The event handler is essentially a helper class that takes care of adding the event listener (by calling addListener and addHandler) and mapping the listener to a response method in the page. Further information on these APIs can be found in the API documentation.

Under the hood the Page class implements the methods specified by the ActionListener interface. In adding an event the page adds itself as a listener to the target component. When the event of interest is fired the page therefore responds to the event via this listener interface. The page then searches for the handler (that you had registered) and invokes it.

Extended Event Specification

As we saw in the earlier data binding chapter the syntax for dynamic method bindings has been extended to include referencing of classes other than the class upon which the current page is based.

The obvious difference from the data binding syntax is that an event binding sets up a reference to a method (the event handler method) and does not itself cause the method to be evaluated. The syntax for the extended event bindings is as follows:

Event specifications

Syntax

Role

mypackage.MyClass.myMethod

to invoke a static method

mypackage.MyClass[].myMethod

to create a new instance of the class on each evaluation

mypackage.MyClass[referenceName].myMethod

for a named object instance

myMethod[referenceName]

for a method contained with the invoking page

[referenceName].myMethod

for a method contained with the class instance referred to by the reference name.

To illustrate this extended event specification syntax let's have a look at an example.

Counter example extended with library functions

  1. <Page class="com.mypackage.ui.swing.SimpleCalculator">
  2. <Components>
  3. <Edit name="Counter" ... />
  4. <Panel ...>
  5. <Button name="DecBtn" ... content="${org.formaria.library.Calculator.getDecLabel()}"/>
  6. <Button name="ClearBtn" content="${org.formaria.library.Calculator[foo].getClearLabel()}"/>
  7. <Button name="IncBtn" ... content="${[foo].getIncLabel()}"/>
  8. <Button name="divideBtn" ... content="/"/>
  9. <Button name="multiplyBtn" ... content="X"/>
  10. </Panel>
  11. </Components>
  12. <Events>
  13. <Event method="doEquals" target="ClearBtn" type="ActionHandler"/>
  14. <Event method="org.formaria.library.Calculator.increment"
  15. target="IncBtn" type="ActionHandler"/>
  16. <Event method="org.formaria.library.Calculator[].decrement"
  17. target="DecBtn" type="ActionHandler"/>
  18. <Event method="org.formaria.library.Calculator[foo].divide"
  19. target="divideBtn" type="ActionHandler"/>
  20. <Event method="[foo].multiply" target="multiplyBtn" type="ActionHandler"/>
  21. </Events>
  22. </Page>

In the above example we are recreating the simple calculator that we have used in other examples. While the example is a contrived (we will document some more realistic examples later) it shows the new syntax in action.

Line 14 sets up an event binding to a static member of the org.formaria.library. Calculator class

Line 15 constructs a new instance of the class each time the event is bound (when the XML expression is loaded and evaluated).

Line 16 constructs a new instance and sets up a reference to the new instance. If the instance already existed it would be reused. In this case a instance of 'foo' had already been created at line 7 so that instance is reused instead of creating a new instance.

Lines 17 and 18 create bindings to other methods in the class and as in the case of line 16 they each use the 'foo' object created on line 7.

The importance of this simple extension should not be under estimated. Quite simply this extension means that your event handlers do not necessarily have to exist in a class derived from Page. In fact the extension means that you can build libraries of utility functions for common tasks and these functions can be used for things like navigation, calculations and other tasks that do not have to be tied to a particular page.

It is also worth noting that this extension mechanism does not mean that the library functions built using the mechanism cannot access the current page or the current component. Such values can be passed as arguments to the function are they can be accessed via the project, the page, the current event and the component hierarchy.

Adding event types

The EventHandler class loads a file events.xml to get a specification of the events it is to handle. The event handler can be instructed to handle other event types by adding additional configuration files. The easiest way to do this on a per project basis is to add an events.xml file to the project's resources. A startup parameter EventHandlers controls the name of the file loaded, but if none is found Aria attempts to load events.xml.

The default events.xml file is as follows:

Default event specification

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <events>
  3. <event name="ActionHandler" interface="java.awt.event.ActionListener" mask="128"/>
  4. <!-- mask="AWTEvent.ACTION_EVENT_MASK"/-->
  5. <event name="MenuHandler" interface="java.awt.event.ActionListener" mask="128"/>
  6. <!-- mask="AWTEvent.ACTION_EVENT_MASK"/-->
  7. <event name="FocusHandler" interface="java.awt.event.FocusListener" mask="4"/>
  8. <!-- mask="AWTEvent.FOCUS_EVENT_MASK"/-->
  9. <event name="TextHandler" interface="java.awt.event.TextListener" mask="1024"/>
  10. <!-- mask="AWTEvent.TEXT_EVENT_MASK"/-->
  11. <event name="ItemHandler" interface="java.awt.event.ItemListener" mask="512"/>
  12. <!-- mask="AWTEvent.ITEM_EVENT_MASK"/-->
  13. <event name="KeyHandler" interface="java.awt.event.KeyListener" mask="8"/>
  14. <!-- mask="AWTEvent.KEY_EVENT_MASK"/-->
  15. <event name="MouseHandler" interface="java.awt.event.MouseListener" mask="16"/>
  16. <!-- mask="AWTEvent.MOUSE_EVENT_MASK"/-->
  17. <event name="MouseMotionHandler" interface="java.awt.event.MouseMotionListener" mask="32"/>
  18. <!-- mask="AWTEvent.MOUSE_MOTION_EVENT_MASK"/-->
  19. </events>