Tutorial - Multi Touch in AS3 / Flash Player 10.1 - Part 2. TouchEvent & MouseEvent Sequences

Hi Everyone,

In this installment I will cover the TouchEvent and how it is intertwined with MouseEvent. First you need to understand that touches will fire MouseEvent's too. You will get a TouchEvent first, then a MouseEvent. This can prove annoying in some cases, and good in others.

For the complete listing of TouchEvent's as well as some basic description of the information it carries please refer to this page.

First let us register the following events on a target Sprite:

  • TouchEvent.TOUCH_BEGIN
  • TouchEvent.TOUCH_OVER
  • TouchEvent.TOUCH_OUT
  • MouseEvent.MOUSE_DOWN
  • MouseEvent.MOUSE_OVER
  • MouseEvent.MOUSE_OUT

The actual series of events goes like this:

  • You apply your finger -- nothing happens
  • You start dragging your finger -- TOUCH_BEGIN, TOUCH_OVER, MOUSE_OVER, MOUSE_DOWN get fired, in that order
  • You move your finger out of the object -- TOUCH_OUT, MOUSE_OUT get fired, in that order
  • You move your finger back on the object -- TOUCH_OVER, MOUSE_OVER get fired, in that order

Stage Action
Just like with MouseEvent.MOUSE_UP, it is better to listen to TouchEvent.TOUCH_END on the Stage. Just as in the MouseEvent.MOUSE_DOWN handler we add a listener to MouseEvent.MOUSE_UP on the Stage; in the TouchEvent.TOUCH_BEGIN handler we add a listener to TouchEvent.TOUCH_END on the Stage.

Then we get one more action that fires event:

  • You remove your finger from the screen -- TOUCH_END, MOUSE_UP get fired, in that order.

Testing it yourself
Try the following code to test it yourself:

Actionscript:
  1. package
  2. {
  3.     import ascb.drawing.Pen;
  4.     import flash.display.Sprite;
  5.     import flash.events.Event;
  6.     import flash.events.MouseEvent;
  7.     import flash.events.TouchEvent;
  8.     import flash.text.TextField;
  9.     import flash.ui.Multitouch;
  10.     import flash.ui.MultitouchInputMode;
  11.    
  12.     /**
  13.      * ...
  14.      * @author Martin Legris ( http://blog.martinlegris.com )
  15.      */
  16.     public class Main extends Sprite
  17.     {
  18.         protected var _tf:TextField;
  19.         protected var _object:Sprite;
  20.        
  21.         public function Main():void
  22.         {
  23.             if (stage) init();
  24.             else addEventListener(Event.ADDED_TO_STAGE, init);
  25.         }
  26.        
  27.         private function init(e:Event = null):void
  28.         {
  29.             removeEventListener(Event.ADDED_TO_STAGE, init);
  30.             _tf = new TextField();
  31.             _tf.width = stage.stageWidth;
  32.             _tf.height = stage.stageHeight;
  33.             _tf.mouseEnabled = false;
  34.             addChild(_tf);
  35.            
  36.             Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
  37.             _tf.text = "Input Mode: " + Multitouch.inputMode + "\n";
  38.             _tf.appendText("Touch Points: " + Multitouch.maxTouchPoints + "\n");
  39.             _tf.appendText("Supports Touch Events: " + Multitouch.supportsTouchEvents + "\n");
  40.            
  41.             createChilds();
  42.         }
  43.        
  44.         protected function createChilds():void
  45.         {
  46.             _object = new Sprite();
  47.             var p:Pen = new Pen(_object.graphics);
  48.             p.clear();
  49.             p.beginFill(0x0033DD, 1);
  50.             p.drawRect(0, 0, 320, 200);
  51.             p.endFill();
  52.             _object.x = 150;
  53.             _object.y = 250;
  54.             addChild(_object);
  55.            
  56.             _object.addEventListener(TouchEvent.TOUCH_BEGIN, doTouchBegin);
  57.             _object.addEventListener(TouchEvent.TOUCH_OVER, doTouchOver);
  58.             _object.addEventListener(TouchEvent.TOUCH_OUT, doTouchOut);
  59.            
  60.             _object.addEventListener(MouseEvent.MOUSE_OVER, doMouseOver);
  61.             _object.addEventListener(MouseEvent.MOUSE_OUT, doMouseOut);
  62.             _object.addEventListener(MouseEvent.MOUSE_DOWN, doMouseDown);
  63.         }
  64.        
  65.         protected function doTouchBegin(evt:TouchEvent):void
  66.         {
  67.             _tf.appendText("touchBegin\n");
  68.             stage.addEventListener(TouchEvent.TOUCH_END, doTouchEnd);
  69.         }
  70.        
  71.         protected function doTouchEnd(evt:TouchEvent):void
  72.         {
  73.             _tf.appendText("touchEnd\n");
  74.             stage.removeEventListener(TouchEvent.TOUCH_END, doTouchEnd);
  75.         }
  76.        
  77.         protected function doTouchOver(evt:TouchEvent):void
  78.         {
  79.             _tf.appendText("touchOver\n");
  80.         }
  81.        
  82.         protected function doTouchOut(evt:TouchEvent):void
  83.         {
  84.             _tf.appendText("touchOut\n");
  85.         }
  86.        
  87.         protected function doMouseOver(evt:MouseEvent):void
  88.         {
  89.             _tf.appendText("doMouseOver\n");
  90.         }
  91.        
  92.         protected function doMouseOut(evt:MouseEvent):void
  93.         {
  94.             _tf.appendText("doMouseOut\n");
  95.         }
  96.        
  97.         protected function doMouseDown(evt:MouseEvent):void
  98.         {
  99.             stage.addEventListener(MouseEvent.MOUSE_UP, doMouseUp);
  100.             _tf.appendText("doMouseDown\n");
  101.         }
  102.        
  103.         protected function doMouseUp(evt:MouseEvent):void
  104.         {
  105.             _tf.appendText("doMouseUp\n");
  106.             stage.removeEventListener(MouseEvent.MOUSE_UP, doMouseUp);
  107.         }
  108.     }
  109. }

TouchEvent.TOUCH_MOVE, MouseEvent.MOUSE_MOVE
This is where, in my opinion, it gets a little strange in my opinion. You cannot expect the TouchEvent to be fired before the MouseEvent; but in any case in common practice you will not listen to both simultaneously. You will either pick one or the other.

To test this yourself, first modify the doTouchBegin and doTouchEnd functions like so:

Actionscript:
  1. protected function doTouchBegin(evt:TouchEvent):void
  2. {
  3.     _tf.appendText("touchBegin("+evt.touchPointID+")\n");
  4.     stage.addEventListener(TouchEvent.TOUCH_END, doTouchEnd);
  5.     stage.addEventListener(TouchEvent.TOUCH_MOVE, doTouchMove);
  6. }
  7.        
  8. protected function doTouchEnd(evt:TouchEvent):void
  9. {
  10.     _tf.appendText("touchEnd("+evt.touchPointID+")\n");
  11.     stage.removeEventListener(TouchEvent.TOUCH_END, doTouchEnd);
  12.     stage.removeEventListener(TouchEvent.TOUCH_MOVE, doTouchMove);
  13. }

Next, let's code the doTouchMove function...

Actionscript:
  1. protected function doTouchMove(evt:TouchEvent):void
  2. {
  3.     _tf.appendText("touchMove("+evt.touchPointID+") [" + evt.stageX + "," + evt.stageY + "]\n");
  4. }

You will notice that I "trace" the touchPointID property of the event; to demonstrate that it is a constant in the case of single touch.

Same deal with the MouseEvent.MOUSE_MOVE, first modify doMouseDown and doMouseUp functions:

Actionscript:
  1. protected function doMouseDown(evt:MouseEvent):void
  2. {
  3.     stage.addEventListener(MouseEvent.MOUSE_UP, doMouseUp);
  4.     stage.addEventListener(MouseEvent.MOUSE_MOVE, doMouseMove);
  5.     _tf.appendText("doMouseDown\n");
  6. }
  7.  
  8. protected function doMouseUp(evt:MouseEvent):void
  9. {
  10.     _tf.appendText("doMouseUp\n");
  11.     stage.removeEventListener(MouseEvent.MOUSE_UP, doMouseUp);
  12.     stage.removeEventListener(MouseEvent.MOUSE_MOVE, doMouseMove);
  13. }

finally, code the doMouseMove function like so:

Actionscript:
  1. protected function doMouseMove(evt:MouseEvent):void
  2. {
  3.     _tf.appendText("doMouseMove [" + evt.stageX + "," + evt.stageY + "]\n");
  4. }

How to disable this intertwining nightmare
In many cases, you will have a different handler for TouchEvents and MouseEvents, and you will not want BOTH to be triggered. This is because in a multi-touch ideology you will have multiple touches to track, meaning many TouchEvents being fired and with a couple of touchPointID's being referenced. Hence your handler cannot simply copy the logic you use in typical mouse handlers.

The method I have found that works best is to remove the mouse listeners on TOUCH_BEGIN and add them again on TOUCH_END, but making sure you add them only once all touches have ended. In most cases the user will use the Mouse or Touch; but not both simultaneously.

First we will need to track how many touches are active by adding a _activeTouches protected property to the class..

Actionscript:
  1. protected var _activeTouches:Number = 0;

Then, in the doTouchBegin handler, increment this property; and in the doTouchEnd handler, decrement it. Next, in the doTouchBegin handler, if the value before incrementation is 0, remove mouse listeners; and in the doTouchEnd handler, if the value after decrementation is 0, add the mouse listeners. Like so:

Actionscript:
  1. protected function registerMouseHandlers():void
  2. {
  3.     _object.addEventListener(MouseEvent.MOUSE_OVER, doMouseOver);
  4.     _object.addEventListener(MouseEvent.MOUSE_OUT, doMouseOut);
  5.     _object.addEventListener(MouseEvent.MOUSE_DOWN, doMouseDown);
  6. }
  7.  
  8. protected function removeMouseHandlers():void
  9. {
  10.     _object.removeEventListener(MouseEvent.MOUSE_OVER, doMouseOver);
  11.     _object.removeEventListener(MouseEvent.MOUSE_OUT, doMouseOut);
  12.     _object.removeEventListener(MouseEvent.MOUSE_DOWN, doMouseDown);
  13. }
  14.  
  15. protected function doTouchBegin(evt:TouchEvent):void
  16. {
  17.     if (_activeTouches == 0)
  18.     {
  19.         removeMouseHandlers();
  20.         stage.addEventListener(TouchEvent.TOUCH_END, doTouchEnd);
  21.         stage.addEventListener(TouchEvent.TOUCH_MOVE, doTouchMove);
  22.     }
  23.    
  24.     _activeTouches++;
  25.     _tf.appendText("touchBegin("+evt.touchPointID+")\n");
  26. }
  27.  
  28. protected function doTouchEnd(evt:TouchEvent):void
  29. {
  30.     _activeTouches--;
  31.    
  32.     if (_activeTouches == 0)
  33.     {
  34.         stage.removeEventListener(TouchEvent.TOUCH_END, doTouchEnd);
  35.         stage.removeEventListener(TouchEvent.TOUCH_MOVE, doTouchMove);
  36.         registerMouseHandlers();
  37.     }
  38.        
  39.     _tf.appendText("touchEnd("+evt.touchPointID+")\n");
  40. }

Did you notice that I add and remove TouchEvent's on the Stage only in certain conditions?
Now that this is out of the way, no more intertwining action! You can concentrate on programming your Touch logic separately from your Mouse logic w/o having fears of interference.

TouchEvent.TOUCH_TAP
The last thing to cover is this event. Basically to show you when it is fired.. Basically, they are fired too often. They are fired even if you keep your finger moving on the screen for 2-3-4 seconds. To me, that is not a TAP. If you press your finger, but do not move, the Windows 7 will transform the touch into a right-click, you will see a circle draw itself around your finger, if you wait for it to be completely drawn: it is a right-click AND a touch, but not a TAP. Otherwise whatever you do, with one single finger, is a TAP.

To demonstrate this, add the following property to your Main class:

Actionscript:
  1. protected var _touchDown:Number;

add this event listener code with the other ones (TouchEvent.TOUCH_BEGIN, TOUCH_OVER, TOUCH_OUT)

Actionscript:
  1. _object.addEventListener(TouchEvent.TOUCH_TAP, doTouchTap);

Next modify doTouchBegin and doTouchEnd like so:

Actionscript:
  1. protected function doTouchBegin(evt:TouchEvent):void
  2. {
  3.     if (_activeTouches == 0)
  4.     {
  5.         removeMouseHandlers();
  6.         stage.addEventListener(TouchEvent.TOUCH_END, doTouchEnd);
  7.         stage.addEventListener(TouchEvent.TOUCH_MOVE, doTouchMove);
  8.        
  9.         _touchDown = new Date().getTime();
  10.     }
  11.    
  12.     _activeTouches++;
  13.     _tf.appendText("touchBegin("+evt.touchPointID+")\n");
  14. }
  15.  
  16. protected function doTouchEnd(evt:TouchEvent):void
  17. {
  18.     _activeTouches--;
  19.    
  20.     if (_activeTouches == 0)
  21.     {
  22.         stage.removeEventListener(TouchEvent.TOUCH_END, doTouchEnd);
  23.         stage.removeEventListener(TouchEvent.TOUCH_MOVE, doTouchMove);
  24.         registerMouseHandlers();
  25.        
  26.         _tf.appendText("touch elapsed time:" + (new Date().getTime() - _touchDown) / 1000 + "\n");
  27.         _touchDown = 0;
  28.     }
  29.        
  30.     _tf.appendText("touchEnd("+evt.touchPointID+")\n");
  31. }

finally, add a doTouchTap handler:

Actionscript:
  1. protected function doTouchTap(evt:TouchEvent):void
  2. {
  3.     _tf.appendText("touchTap(" + evt.touchPointID + ")\n");
  4. }

If you run this updated code, you will notice that no matter how much your move your finger around once it is done, and how long you keep it down, it will fire a TOUCH_TAP event.. useless in my opinion.

Oki, that's it for today. Hope you enjoyed! I will keep going next week.

Martin



Related posts (automatically generated):

  1. Tutorial - Multi Touch in AS3 / Flash Player 10.1 - Part 1. Setting Up


4 Responses to “Tutorial - Multi Touch in AS3 / Flash Player 10.1 - Part 2. TouchEvent & MouseEvent Sequences”

  1. Hannes says:

    Hello,
    great tutorial

    can you please also show us how to move a object(image) with touch support?

    thx

  2. Metachoir says:

    wow great!!!
    thx~

  3. Gemma18MK says:

    Very useful article about this post. I try essay writing services and buy custom essay papers or essays written about this good post.

  4. TheDeadKoiClub says:

    Nice tutorial, but I must disagree. I’m working with a Dell Latitude XT2 and touch events are not always fired in the the same order with mouse events on my machine. Actually it seems to work most of the times but every now and then the order is switched. I’m not quite sure if this may be hardware dependent (or at least caused somehow by the system and not by the Flash Player). I think the better solution by Adobe would have been to implement a switch to simply disable this behaviour when needed.

Leave a Reply