Survive your Flex interview – part three: how to prevent event propagation and what’s the difference between stopPropagation and stopImmediatePropagation.

This question is about a bit more advanced event processing. In the first part we mentioned that event goes always through capture, target and bubbling phase. If for some reasons you want to stop propagation, you should “capture” an event and stop it from going further. This will work for any phase.

To do that you will use two methods of the Event class: stopPropagation and stopImmediatePropagation. Both will stop the event at the same event listener object. The difference is – stopPropagation will let other listener functions in the same object to be used, while stopImmediatePropagation will stop an event in the listener function from which it was used.

Look at the example:

object.addEventListener( MouseEvent.CLICK, functionOne );

object.addEventListener( MouseEvent.CLICK, functionTwo );

If functionOne contains event.stopPropagation, functionTwo will be called as well. If it contains event.stopImmediatePropagation, functionTwo will be ignored.

For more information please, visit http://livedocs.adobe.com/flex/3/html/help.html?content=events_08.html.

Survive your Flex interview – part two: event currentTarget vs target.

This question you may hear together with question about event phases. Difference is:

  • target property contains a reference to the display object reached during target phase. For example – for MouseEvent.CLICK event, dispatched during for example clicking a button, this will point the object that was clicked – in this case our button.
  • currentTarget contains reference to the object that is currently processed during all phases. This means that in targeting phase this will be the same object, i.e. our button, while during capture and bubbling phase this will reference to many different objects, depending where the event is at that moment – from a stage object down through its children to target object.

Survive your Flex interview – part one: events dispatching and phases.

In series of posts I will try to bring you closer to what you can hear during your Flex developer interview. You will hear most of them, depending on where are you trying to get a job and who will question you. I will try to present you topics from basic Actionscript to a bit more advanced Flex issues.

Although this question is more about Actionscript, you will hear it at 99% of Flex developer interviews. Ok, let’s get to the point.

Event system in Actionscript consists of three elements: dispatchers, listeners and event objects. Event dispatcher is the subject of the event, event is the event object encapsulating details of the event occurrence (can pass additional data to listeners) while listener is the handler function for dealing with an event.

If you are familiar with Java design patterns this is also known as Observer Pattern. In Java nomenclature these are known as subjects, observers and event objects. When names are different, the idea stays the same.

Key for dispatching events and notifying listeners is the EventDispatcher class. Functionality of that class covers the following (methods defined by IEventDispatcher interface):

  • addEventListener – for registering listener objects,
  • dispatchEvent – dispatches events,
  • hasEventListener – check whether the EventListener has any listeners defined by the parameter,
  • removeEventListener – removes listener from an EventDispatcher object,
  • willTrigger – checks whether an event listener is registered with EventDispatcher.

You can use EventDispatcher by implementing its methods in your class or – more common approach – by extending EventDispatcher class.

If the user interacts with the application, dispatching an event like clicking a button will affect many different objects. This happens because Actionscript 3.0 introduced event propagation – a mechanism of propagating events down from a stage through all objects to their children up to our button in this example.

Events starts with capturing phase, starting from top most parent object – stage. During this phase all objects, down to the one where event originated, are notified of the event. Difference is that event object has event.eventPhase property set to EventPhase.CAPTURING_PHASE.

Than event phase is changed to targeting phase. This phase means that event is at the target object or the object from which event originated. This phase relates always to only one – that object.

After reaching target object, event “goes back” through all the parents again. This phase is called the bubbling phase.

What can be added? Listening to capture phase is pretty rare. In most cases, you will use the following:

object.addEventListener( “SomeEvent”, eventListenerFunction );

This is the default behavior. It means event will be listened at target and bubbling phases. To listen event at capture phase, add a third parameter like in example:

object.addEventListener( “SomeEvent”, eventListenerFunction, true );

For more information, you may want to use the following: http://www.adobe.com/devnet/actionscript/articles/event_handling_as3.html

Flex, AIR 2.0, multitouch and Google Maps

When I first heard about possibilities of AIR 2.0 after MAX Conference I got enthusiast, specially about features that were so awaited. Among them, one of the most promising for the future way of creating applications with rich interfaces is – multitouch support. Thanks to the updated AIR 2.0 we have access to gestures on when using touch pads and multitouch screen displays. I was looking forward to create very simple demo using multitouch and I thought about mapping using Google Maps.

Here is the simple recipe how to create the simplest application using three gestures: Pan (with two fingers), Zoom (also with two fingers) and Swipe (using three fingers). I built it and tested on MB Pro with multitouch running under Snow Leopard with AIR 2.0 beta 1.

Ok, let’s get started. First – we need to set up a map. I almost pasted the code from the Google site to make it simpler for you. I listen to to the event mapevent_mappreinitialize, where I set up map types, zoom etc.:

private function onMapInitialize( event : MapEvent ) : void
{
mapTypes = MapType.DEFAULT_MAP_TYPES;
 
var mapOptions : MapOptions = new MapOptions();
 
mapOptions.zoom = 14;
mapOptions.mapType = mapTypes[ currentMapType ];
 
map.setInitOptions( mapOptions );
}

Ok, than we set up the map itself on mapevent_mapready.  We will look for The Ministry of Ideas in Cracow and put a marker there:

private function onMapReady( event : MapEvent ) : void
{
doGeocode();
}
 
private function doGeocode() : void
{
var geocoder : ClientGeocoder = new ClientGeocoder();
geocoder.addEventListener( GeocodingEvent.GEOCODING_SUCCESS, function ( event : GeocodingEvent ) : void
{
var placemarks : Array = event.response.placemarks;
if ( placemarks.length > 0 )
{
map.setCenter( placemarks[ 0 ].point );
var marker : Marker = new Marker( placemarks[ 0 ].point );
marker.addEventListener( MapMouseEvent.CLICK, function ( event : MapMouseEvent ) : void
{
marker.openInfoWindow( new InfoWindowOptions( { content: placemarks[ 0 ].address } ) );
} );
map.addOverlay( marker );
 
var latlng : LatLng = marker.getLatLng();
 
map.panTo( latlng );
}
} );
geocoder.addEventListener( GeocodingEvent.GEOCODING_FAILURE, function ( event : GeocodingEvent ) : void
{
Alert.show( "Geocoding failed: " + event.toString() );
} );
geocoder.geocode( "Mostowa 2, Krakow, Poland" );
}

Now, when the map is set up it’s time to do the magic. On creationComplete for Application we add:

public function onCreationComplete() : void
{
Multitouch.inputMode = MultitouchInputMode.GESTURE;
 
this.addEventListener( TransformGestureEvent.GESTURE_ZOOM, onZoom );    // zoom map in/out
this.addEventListener( TransformGestureEvent.GESTURE_SWIPE, onSwipe );    // change map type
this.addEventListener( TransformGestureEvent.GESTURE_PAN, onPan );        // pan map around
}

We also use a constant to set up threshold for reading the gestures and a variable to hold the last update time. We need them so the application feedback is comfortable to the client:

private static const IDLE_THRESHOLD : int = 300;
private var lastUpdate : Number = 0;

Zooming is simple. If the zoom variables from the gesture event are below 1 – we zoom out, if greater than 1 we zoom in. Simple as that. We have all we need in the event variables.

private function onZoom( event : TransformGestureEvent ) : void
{
if( getTimer() - lastUpdate > IDLE_THRESHOLD )
{
if( event.scaleX > 1 && event.scaleY > 1 )
map.setZoom( map.getZoom() + 1 );
if( event.scaleX < 1 && event.scaleY < 1 )
map.setZoom( map.getZoom() - 1 );
 
lastUpdate = getTimer();
}
}

Than we go to panning. Panning is also easy. What I decided to do is to read a movement from the event and create a new “vector” based on it. I use it to set up a new center of the map:

private function onPan( event : TransformGestureEvent ) : void
{
var point : Point = map.fromLatLngToPoint( map.getCenter() );
var newPoint : Point = new Point( point.x + ( event.offsetX * 10 ), point.y + ( event.offsetY * 10 ) );
var newLatLon : LatLng = map.fromPointToLatLng( newPoint );
 
map.setCenter( newLatLon );
}

I thought that we could use swiping for changing the map type, which is quite useful as Google Maps API has four map types. when you swipe right or left the map type will change. As you can see I use helper variable to hold the current map type.

private function onSwipe( event : TransformGestureEvent ) : void
{
if( getTimer() - lastUpdate > IDLE_THRESHOLD )
{
if( event.offsetX > 0 )
currentMapType++;
if( event.offsetX < 0 )
currentMapType--;
 
if( currentMapType < 0 )
currentMapType = mapTypes.length - 1;
 
currentMapType = currentMapType % mapTypes.length;
 
map.setMapType( mapTypes[ currentMapType ] );
 
lastUpdate = getTimer();
}
}

You will find the full source code here. Comments and critique are welcomed, as always.

You can try it by quickly installing the following AIR file. Have in mind that to test it you will need a computer with a multitouch screen or pad and a PC with Windows 7 or a Mac with Snow Leopard.

Please upgrade your Flash Player

Have a great day,
W.