One of the navigation methods unique to mobile devices is the
ability to interact with an application via gestures on the device’s touch
screen. Multi-touch is defined as the ability to simultaneously register
three or more touch points on the device. Within Adobe AIR 2.6, there are
two event classes used to listen for multi-touch events.
1. GestureEvent
The GestureEvent class is used to listen for a
two-finger tap on the device. GESTURE_TWO_FINGER_TAP is the event used to
listen for this action. This event will return the registration points
for the x and y coordinates when a two-finger tap occurs for both stage
positioning as well as object positioning.
Let’s review the code that follows. Within
applicationComplete of the application, an event handler function is called; it first sets the
Multitouch.inputMode to
MultitouchInputMode.GESTURE. Next, it
checks to see if the device supports multi-touch by reading the
static property of the Multitouch class. If this
property returns as true, an event listener is added to the stage to
listen for GestureEvent.GESTURE_TWO_FINGER_TAP events.
When this event occurs, the
onGestureTwoFingerTap method is called. The
onGestureTwoFingerTap method will capture
the localX and localY
coordinates, as well as the stageX and
stageY coordinates. If you two-finger tap on an
empty portion of the stage, these values will be identical. If you
two-finger tap on an object on the stage, the
localX and localY coordinates
will be the values within the object, and the
stageX and stageY will be
relative to the stage itself. See Figure 1 for an example of a two-finger tap
on the stage and Figure 2 for a
two-finger tap on the Android image.
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
applicationComplete="application1_applicationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected function application1_applicationCompleteHandler
(event:FlexEvent):void {
Multitouch.inputMode = MultitouchInputMode.GESTURE;
if(Multitouch.supportsGestureEvents){
stage.addEventListener(GestureEvent.GESTURE_TWO_FINGER_TAP,
onGestureTwoFingerTap);
} else {
status.text="gestures not supported";
}
}
private function onGestureTwoFingerTap(event:GestureEvent):void {
info.text = "event = " + event.type + "\n" +
"localX = " + event.localX + "\n" +
"localX = " + event.localY + "\n" +
"stageX = " + event.stageX + "\n" +
"stageY = " + event.stageY;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Label id="status" text="Do a 2 finger tap both on and off the object"
top="10" width="100%" textAlign="center"/>
<s:TextArea id="info" width="100%" top="40" editable="false"/>
<s:Image width="384" height="384" bottom="10" horizontalCenter="0"
source="@Embed('android_icon.png')"/>
</s:Application>
2. TransformGesture
There are multiple transform gesture events available within AIR
2.6. Each will capture a unique multi-touch event. The next example
demonstrates how to listen for GESTURE_PAN, GESTURE_ROTATE, GESTURE_SWIPE, and GESTURE_ZOOM events.
Let’s review the following code. Within
applicationComplete of the application, an event handler function is called; it first sets the
Multitouch.inputMode to MultitouchInputMode.GESTURE. Next, it
checks to see if the device supports multi-touch by reading the
static property of the Multitouch class. If this
property returns as true, event listeners are added to the stage to
listen for the TransformGestureEvent.GESTURE_PAN, TransformGestureEvent.GESTURE_ROTATE, TransformGestureEvent.GESTURE_SWIPE, and
TransformGestureEvent.GESTURE_ZOOM
events.
When a user grabs the object with two
fingers and drags the object, the TransformGestureEvent.GESTURE_PAN event is triggered
and the onGesturePan method is called.
Within the onGesturePan method, the
offsetX and offsetY values of
the event are written to the text property of the
TextArea component. Adding the
offsetX and offsetY values
returned from the event to the object’s x and
y properties will move the object across the stage.
The results are shown in Figure 3.
When a user grabs the object with two
fingers and rotates the object, the TransformGestureEvent.GESTURE_ROTATE
event is triggered and the onGestureRotate method
is called. Within the onGestureRotate
method, the rotation value of this event is written to the text property
of the TextArea component. To allow the object to
rotate around its center, the object’s
transformAround method is called, and the event’s
rotation value is added to the object’s rotationZ
value. The results are shown in Figure 4.
When a user swipes across the object with one finger in any
direction, the TransformGestureEvent.GESTURE_SWIPE event is
triggered and the onGestureSwipe method is called.
Within the onGestureSwipe method, the value of the
event’s offsetX and offsetY is
evaluated to determine which direction the user swiped across the
object. This direction is then written to the text property of the
TextArea component. The results are pictured in Figure 5.
When a user performs a “pinch and zoom” on the object with two
fingers, the TransformGestureEvent.GESTURE_ZOOM
event is triggered and the onGestureZoom method is
called. Within the onGestureZoom method, the value of
the event’s scaleX and scaleY
is written to the text property of the TextArea
component. The scaleX value is then used as a
multiplier on the object’s scaleX and
scaleY properties to increase or decrease the size
of the object as the user pinches or expands two fingers on the object.
The results are shown in Figure 6.
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
applicationComplete="application1_applicationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected function application1_applicationCompleteHandler
(event:FlexEvent):void {
Multitouch.inputMode = MultitouchInputMode.GESTURE;
if(Multitouch.supportsGestureEvents){
image.addEventListener(TransformGestureEvent.GESTURE_PAN,
onGesturePan);
image.addEventListener(TransformGestureEvent.GESTURE_ROTATE,
onGestureRotate);
image.addEventListener(TransformGestureEvent.GESTURE_SWIPE,
onGestureSwipe);
image.addEventListener(TransformGestureEvent.GESTURE_ZOOM,
onGestureZoom);
} else {
status.text="gestures not supported";
}
}
private function onGesturePan(event:TransformGestureEvent):void{
info.text = "event = " + event.type + "\n" +
"offsetX = " + event.offsetX + "\n" +
"offsetY = " + event.offsetY;
image.x += event.offsetX;
image.y += event.offsetY;
}
private function onGestureRotate( event : TransformGestureEvent ) : void {
info.text = "event = " + event.type + "\n" +
"rotation = " + event.rotation;
image.transformAround(new Vector3D(image.width/2,image.height/2, 0),
null,
new Vector3D(0,0,image.rotationZ + event.rotation));
}
private function onGestureSwipe( event : TransformGestureEvent ) : void {
var direction:String = "";
if(event.offsetX == 1) direction = "right";
if(event.offsetX == −1) direction = "left";
if(event.offsetY == 1) direction = "down";
if(event.offsetY == −1) direction = "up";
info.text = "event = " + event.type + "\n" +
"direction = " + direction;
}
private function onGestureZoom( event : TransformGestureEvent ) : void {
info.text = "event = " + event.type + "\n" +
"scaleX = " + event.scaleX + "\n" +
"scaleY = " + event.scaleY;
image.scaleX = image.scaleY *= event.scaleX;
}
protected function button1_clickHandler(event:MouseEvent):void
{
image.rotation = 0;
image.scaleX = 1;
image.scaleY = 1;
image.x = 40;
image.y = 260;
info.text = "";
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Label id="status" text="Transform Gestures" top="10" width="100%"
textAlign="center"/>
<s:HGroup width="100%" top="40" left="5" right="5">
<s:TextArea id="info" editable="false" width="100%" height="200"/>
<s:Button label="Reset" click="button1_clickHandler(event)"/>
</s:HGroup>
<s:Image id="image" x="40" y="260" width="400" height="400"
source="@Embed('android_icon.png')"/>
</s:Application>