Flash Navigation Control AS 3.0
We have already discussed how to handle mouse-mashing clicks using ActionScript 2.0. The more structured object oriented nature of ActionScript 3.0 requires an update to this methodology, but it is still pretty simple.
The challenge with ActionScript 3.0 is that you want to be able to trigger a lock or unlock action at any time. You no longer can define a function at _level0, and then call it from anywhere. This change is a huge improvement from a perspective of design patterns, maintainability, and modularity of your objects. It does however mean the methods outlined in the ActionScript 2.0 tutorial will need to be reworked.
The SolutionIn order to work this out, we will want to dispatch events up to a parent listener for our locks and our unlocks. Because these events could be dispatched from classes deep within the system, you will want a predefined constant that you can use to ensure your event type is correct.
Define a Constants Class
The first matter is quite easy to handle. I like to create a Constants class that has public static variables. Then you include that class throughout your application.
package classes
{
public class Constants
{
public static var LOCKEVENT:String = "LockNavigation";
public static var UNLOCKEVENT:String=”UnlockNavigation”;
//other constants may be used depending on your application needs
}
}
The code above assumes that you have a directory called classes at the root of your project. I generally like to have all my class code contained within that directory for project-specific classes.
Creating Your Fill Layer
You will need to have a display object that is at a depth higher than all other content in your application.
The following example assumes you have a parent class that is inheriting from a DisplayObject. It assumes a class variables mainContents and myClickManager. It makes use of a class we will be describing later on called ClickManager.
public function build()
{
mainContents = new Sprite();
//code to set up main contents
this.addChild(mainContents);
/*set up our click control on top. Because we are adding this after our main contents it will be in front*/
myClickManager = new ClickManager(0);
this.addChild(myClickManager);
}
That will put a click manager DisplayObject in front of the main contents of your flash application. You will however notice that we have no way at this point to detect when to lock or unlock clicks.
Listening For Lock and Unlock
We now need to just listen in for events that are dispatched from the system itself. The following update to our parent class will get it ready to go.
public function build()
{
mainContents = new Sprite();
mainContents.addEventListener(Constants.LOCKEVENT, lockClicks);
mainContents.addEventListener(Constants.UNLOCKEVENT, unlockClicks);
//code to set up main contents
this.addChild(mainContents);
/*set up our click control on top. Because we are adding this after our main contents it will be in front*/
myClickManager = new ClickManager(0);
this.addChild(myClickManager);
}
private function lockClicks(e:Event):void
{
if(myClickManager)
{
myClickManager.lockClicks();
}
}
private function unlockClicks(e:Event):void
{
if(myClickManager)
{
myClickManager.unlockClicks();
}
}
Dispatching a Lock and Unlock Commands
ActionScript 3.0 has introduced a different method for handling events. It is useful to note that anything that inherits from DisplayObject (MovieClip, Sprite, etc) has DispatchEvent and AddEventListener included in its inherited functionality. Rather than broadcasting a string as was done in ActionScript 2.0, you dispatch a full event. Further explanation of this will be covered in a later article. Suffice it to say that we have a much more flexible means for handling events in ActionScript 3.0.
In order to get what we want to work, we will need to take advantage of the properties on our event that will make them bubble up until they are caught. If the bubbles property is false, then an event will either be caught by the dispatcher’s immediate parent, or just fizzle out. By setting the bubble value to true, we can dispatch an event deep inside our system, and catch it at the parent level.
So assume that you have a class that is an arbitrary depth inside the solution. This class then runs a Tween when a button is clicked, and we want to prevent click events while the Tween is running. We could have code as follows inside our system:
private function btnClick(e:MouseEvent):void
{
var mover:Sprite = e.currentTarget as Sprite;
navTween = new Tween (mover,"y",Regular.easeOut,
myMover.y,myMover.y+100,30,false);
navTween.addEventListener(TweenEvent.MOTION_FINISH,
tweenDone);
dispatchEvent(new Event(Constants.LOCKEVENT,true,
true));
}
private function tweenDone(e:TweenEvent):void
{
dispatchEvent(new Event(Constants.UNLOCKEVENT,true,
true));
//first boolean sets it as a bubbling event
//second boolean means you could cancel it before
it bubbles all the way up if you want to.
}
Note that in order for this code to work, the DisplayObject that dispatches the event must be added to the DisplayObject. In this example, that is implicit by the fact that the user can see a button to click on it. Also note that the code above assumes navTween is a class variable for the DisplayObject we are working with. Garbage collection can be aggressive in ActionScript 3.0, and a Tween that is a local variable could get cleaned up before it finishes executing. Hopefully it will save you a few headaches to be aware of that going into things.
Click Manager
One of the powers of object oriented design patterns is that you have self-contained modules. At this point, you are free to have any type of behavior you want for your ClickManager class. The ClickManager as I will be showing it takes a parameter that sets the alpha of our fill so we can test it as needed during development. You can extend or customize your ClickManager class to include different behaviors and features without worrying about how it could influence other parts of your code.
package classes
{
import flash.display.Sprite;
public class ClickManager extends Sprite
{
private var lockCount:int;
public function ClickManager(clipAlpha:Number=0)
{
this.graphics.beginFill(0xFFFFFF,clipAlpha);
this.graphics.drawRect(-10000,-10000,20000,20000);
lockCount=0;
this.visible=false;
}
public function lockClicks()
{
lockCount++;
this.visible=true;
}
public function unLockClicks()
{
lockCount--;
if(lockCount<=0)
{
lockCount=0;
this.visible=false;
}
}
public function overRideLockedState()
{
lockCount=0;
this.visible=false;
}
}
}
There are a few notable differences between the AS 2.0, and AS 3.0 versions here. AS 2.0 required that you set a click event on a MovieClip in order for it to block clicks behind it. AS 3.0 however does not. We can turn the lock on or off by simply changing our visible property. I also added a public function to override the locked state.
Important Reminder
Remember that this implementation requires the developer to ensure that every call to lock the navigation has a corresponding call to unlock the system. For a full review of the ideas behind this, see the Navigation Control ActionScript 2.0 version tutorial. The overRideLockedState can be useful if you have code where you know for certain that you can safely kill the locked state. Another option you could explore quite easily is using setTimeOut functionality to ensure that it is never locked beyond a set time limit.
Final Thoughts
The simple solution of covering your clickable areas to disable them is a huge time saver that can improve the stability of your flash applications quite easily. It can be a chore to ensure you have matching locks and unlocks, but even that challenge is simpler than setting up code that disables click events for all clickable items, and then re-enables them later. Migrating the solution from the ActionScript 2.0 version to ActionScript 3.0 is a good learning exercise to get oriented to the different, more structured, way of coding.













