Category: Flex with JAVA
In object-oriented languages, if object A needs to notify object B about some important event, it’s
done using a so-called Observer design pattern; Java implements this pattern in the Observer interface
and Observable class. An observable object may have something interesting going on, and
other objects that want to know about this implement the Observer interface and register themselves
with the observable class.
AS3 implements this design pattern using an event model. Objects can trigger events to each other.
System events are triggered by the AVM, while others are triggered as a result of user interactions
with your application, such as a click on a button or a mouse move. Below are some situations
when events can be dispatched (triggered):
- When Flash Player finishes creating a component, it dispatches a creationComplete event.
The DisplayObject class is a subclass of the EventDispatcher class, which means that each of
the display objects can register an event listener and process events as they occur.
- User-defined classes can dispatch events according to the business logic of the application.
- The EnterFrame event is triggered by the AVM at the application’s frame rate. In movie-type
applications it’s being dispatched when the playhead is entering a new frame. Even though
Flex applications don’t have multiple frames, this event is continuously dispatched. This
makes it a good candidate to check if some important action has occurred, i.e., a Boolean flag
indicating the status of some business transaction is set to true.
- The objects of your application can send custom events to each other.
In an MXML application you’d just specify the name of the event and its handler function (or inline
code) in the attribute of the component without worrying too much about the underlying
CHAPTER 4
RIA WITH ADOBE FLEX AND JAVA 139
AS3 code. But all Flex events are subclasses of flash.events.Event, and in AS3 programs you should
register an event listener to “listen” to this event and write a function to handle this event when it
arrives. For example, the next code snippet specifies that Flash Player has to call a method onEnteringFrame
(written by you) on each EnterFrame event:
addEventListener(Event.ENTERFRAME, onEnteringFrame);
In Java, event listeners are objects, but in AS3 only functions or methods can listen to the events.
If you need to trigger an event from the ActionScript class, it has to be either a subclass of Event-
Dispatcher or implement the IEventDispatcher interface. The latter is the more preferred way because
AS3 doesn’t support multiple inheritance, and your class may need to extend another class.
The other reason to use a lighter interface is that you may not need all the functionality that was
defined in the EventDispatcher class and implement the required interface method as you see fit.
For example:
class MyClass extends HisClass implements IEventDispatcher{
}
Event Flow
When the event listener calls an event handler function, it receives an event object as a parameter,
which contains various attributes of the event, and the most important one is the event’s target.
This terminology might be a little confusing to Java developers since they refer to the component
that generates an event as an event source. But here we say that all events are generated by the
Flash Player. They are initiated at the stage level and flow to the target component (capturing the
stage), i.e., Button, Shape, etc.. After reaching the target, the event “bubbles” its way through to the
parents. So, when you click on a button, we can say that a button is the event target. If a button is
located on a panel, this event will flow from the stage to the panel and then to the button - and
then all the way back.
Flash Player 9 implements an event model based on the World Wide Web Consortium’s (W3C) specification
entitled Document Object Model Events available at http://www.w3.org/TR/DOM-Level-
3-Events/events.html. According to this document, the lifecycle of an event that deals with display
objects consists of three phases: capture, target, and bubbling.
- Capture: During this phase, Flash Player makes a first pass to check every object from the root
of the display list to the target component to see if any parent component might be interested
in processing this event. By default, events are ignored by the parents of the target component
at the capture phase.
- Target: At this phase, event object properties are set for the target and all registered event
listeners for this target will get this event.
Learning Flex Through Applications
140 RIA WITH ADOBE FLEX AND JAVA
- Bubbling: Finally, the event flows back from the target component all the way up to the root
to notify all interested parties identified during the capture phase. Not all events have a bubbling
phase and you should consult the AS3 language reference for the events you’re interested
in.
The three event phases described above don’t apply to the user-defined events because Flash Player
9 doesn’t know about parent-child relations between user-defined event objects. But AS3 developers
can create custom event dispatchers, if they want to arrange event processing in three phases.
Event Propagation or Bubbling
Consider the following sample MXML code with a button in a panel.
<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” >
<mx:Panel x=”17.5″ y=”20″ width=”209″ height=”142″ layout=”absolute”
click=”trace(‘click event in the Panel’)” title=”Just a Panel”>
<mx:Button label=”ClickMe” x=”60.5″ y=”38″
click=”trace(‘click event in the Button’)”/>
</mx:Panel>
</mx:Application>
Listing 4.38 Events.mxml
Run this simple program in the debug mode to enable trace and it’ll show you the following output:
Figure 4.14 Output of the Events.mxml
Clicking on the button in the Flex Builder’s console will show the following:
CHAPTER 4
RIA WITH ADOBE FLEX AND JAVA 141
click event in the Button
click event in the Panel
This illustrates events propagation or bubbling: the click event bubbles up from the target (button)
to its parent (panel).
Now let’s create another version of this application, where the button and the panel as well as the
event processing are coded in AS3. This way you’ll see and appreciate all the legwork that MXML
did for us:
<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”
creationComplete=”onCreationComplete();” >
<mx:Script>
<![CDATA[
import mx.controls.Button;
import mx.containers.Panel;
private var myButton:Button;
private var myPanel:Panel;
// An event handler for creationComplete event
private function onCreationComplete():void {
myPanel=new Panel();
myPanel.width=200;
myPanel.height=150;
myPanel.title=”Just a Panel”;
addChild(myPanel);
myButton = new Button();
myButton.label = “Click me”;
addChild (myButton);
}
]]>
</mx:Script>
</mx:Application>
Listing 4.39 Creating a button and a panel in ActionScript
Learning Flex Through Applications
142 RIA WITH ADOBE FLEX AND JAVA
If you run this code, the output will look like this:
Figure 4.15 A button and a panel without nesting
This is not exactly the result we were looking for and the reason is simple: both the panel and the
button were added to the application’s display list independently. Let’s fix this by adding the button
to the panel by replacing addChild(myButton) with myPanel.addChild(myButton).
Now the hierarchy of the nodes in the display list will be different, and the node representing the
button will be created under the parent node of the panel as shown in Figure 4.16.
Figure 4.16 A button and a panel with nesting
This is much better, but still not exactly the same as Figure 4.14. Let’s try to set the coordinate of the
CHAPTER 4
RIA WITH ADOBE FLEX AND JAVA 143
button to values from Listing 4.38:
myButton.x=60.5;
myButton.y=38;
It did not help because, by default, the panel container uses the vertical layout (see Chapter 3) and
ignores the absolute coordinates. Let’s add one line to change it into an absolute layout:
myPanel.layout=”absolute”;
Now if you run the application it’ll look the same as in Figure 4.13.
We’ve written a lot of ActionScript code, and we haven’t even processed the click events yet! We still
need to add event listeners and event handlers to the button and the panel:
<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”
creationComplete=”onCreationComplete();” >
<mx:Script>
<![CDATA[
import mx.controls.Button;
import mx.containers.Panel;
import flash.events.MouseEvent;
private var myButton:Button;
private var myPanel:Panel;
// An event handler for creationComplete
private function onCreationComplete():void {
myPanel=new Panel();
myPanel.width=200;
myPanel.height=150;
myPanel.layout=”absolute”;
myPanel.title=”Just a Panel”;
addChild(myPanel);
myButton = new Button();
myButton.label = “Click me”;
myButton.x=60.5;
myButton.y=38;
myPanel.addChild (myButton);
Learning Flex Through Applications
144 RIA WITH ADOBE FLEX AND JAVA
// Adding the click event processing
myButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler);
}
// The button click handler (the target phase)
private function buttonClickHandler(event:MouseEvent) :void{
trace (”click event in the Button”);
}
// The panel handler to demo bubbling
private function panelClickHandler(event:MouseEvent) :void{
trace (”click event in the Panel”);
}
]]>
</mx:Script>
</mx:Application>
Listing 4.40 AS_Bubbling.mxml
Run this application in debug mode and the console screen will look the same as the MXML version
of our application:
click event in the Button
click event in the Panel
The order of these messages is a clear indication that the target event was processed first and the
panel responded in the bubbling phase.
Typically, during the capture stage event, listeners on the parent components aren’t called, but
there’s a version of the addEventListener()method that can request calling the listeners during the
capture phase. To turn on event handling during the capture phase, you should use the three-arguments
version of the addEventListener() function in the panel:
myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler, true);
When the third argument equals true, it tells the Flash Player that we are registering this particular
listener for the capture phase (there’s no way to do this in MXML). Now run the application through
the debug mode again and you’ll see that the panel responds first, then the target button. There’s
no event processing in the bubbling phase.
click event in the Panel
click event in the Button
CHAPTER 4
RIA WITH ADOBE FLEX AND JAVA 145
If you’d like to process parent events during both the capture as well as bubbling phase, register two
listeners for the panel - one with the three arguments and one with two. These listeners
myButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler, true);
myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler);
will produce the output proving that the panel has processed the button click event twice: during
both the capture and bubbling phases.
click event in the Panel
click event in the Button
click event in the Panel
Let’s make a change in the panel event handler to show how you can prevent the event from being
delivered to the target if something bad has occurred (from a business application perspective):
private function panelClickHandler(event:MouseEvent) :void{
var badNews: Boolean = true; // a flag to emulate a bad situation
if (event.eventPhase == EventPhase.CAPTURING_PHASE){
trace (”Capturing phase: click event in the Panel”);
if (badNews){
trace (”Capturing phase: Bad news. Will not propagate click to But
ton”);
event.stopPropagation();
}
}else {
trace (”click event in the Panel”);
}
}
The stopPropagation() method can be called at any phase of the event flow. The line
myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler);
means “listen to the click event, and when it’ll occur, call the function panelClickHandler.”
Handling events was so much easier in MXML…
Why do we even need to know about these addEventListener() function calls? Well, first, there are
some classes that don’t have equivalents in MXML, hence you don’t have a choice. Second, if your
program is adding components dynamically, addListener() is your only choice since there’s no way to
use MXML notation there. And third, you may prefer writing your components only in AS3.