Actionscript 3: Listeners not working after child is removed, then added again - actionscript-3

I have some code that adds a "back" button to the stage that has its own code, and clicking it removes the button and brings users back to a title screen. However, when the "back" button is reintroduced to the stage, none of its listeners work.
public class BACK extends SimpleButton {
public function BACK() {
// constructor code
trace('back button on stage');
addEventListener(Event.ADDED_TO_STAGE, startUp);
}
function startUp(ev:Event): void{
addEventListener(MouseEvent.CLICK, gotoTitle);
addEventListener(Event.REMOVED_FROM_STAGE, backBtnCleanUp);
}
function gotoTitle(ev:MouseEvent): void{
trace('gototitle called');
MovieClip(root).gotoTitle();
}
function backBtnCleanUp(ev:Event): void{
trace('back button cleanup called');
removeEventListener(Event.ADDED_TO_STAGE, startUp);
removeEventListener(MouseEvent.CLICK, gotoTitle);
removeEventListener(Event.REMOVED_FROM_STAGE, backBtnCleanUp);
}
}
the trace function executes when it is first added to the stage, but not when it is added again after being removed. This is the code (from Main) that both adds and removes it.
function gotoHelp(): void{ // transitions to the help screen
cleanTitle();
addChild(helpBG);
addChild(backBtn);
backBtn.x = 550;
backBtn.y = 200;
}
function gotoTitle(): void{ //goes to the title screen
trace('going to title');
removeChild(backBtn);
removeChild(helpBG);
titleStartUp();
}

You should create a new instance of your BACK button every time you need to show it, or remove this line from backBtnCleanUp function : ( not tested )
removeEventListener(Event.ADDED_TO_STAGE, startUp);

Related

Flex topLevelApplication and SuperTabNavigator mouse event handler

I am using the flex SuperTabNavigator and want on closing the tab check if the control button was pressed. I tried:
public static var CONTROL_PRESSED:Boolean = false;
public function init():void {
var clickListener:Function = function _clickListener(event:MouseEvent):void{
trace(event.ctrlKey);
if(event.ctrlKey){
CONTROL_PRESSED = true;
}else{
CONTROL_PRESSED = false;
}
};
FlexGlobals.topLevelApplication.addEventListener(MouseEvent.CLICK, clickListener);
}
The problem with this is that the mouse click is called everywhere in the application except on the tab. I also tried the same code but addEventListener(MouseEvent.CLICK, clickListener); to add the listener to the SuperTabNavigator and it did not work at all. Is there another way to catch the mouse click?
This is because the SuperTabNavigator has a private mouse click handler function that hides the MouseEvent:
private function closeClickHandler(event:MouseEvent):void {
if(this.enabled) {
dispatchEvent(new Event(CLOSE_TAB_EVENT));
}
event.stopImmediatePropagation();
event.stopPropagation();
}
You'll need to modify the SuperTab class in the SuperTabNavigator source to dispatch some CustomEvent with the data you want instead of a plain new Event(CLOSE_TAB_EVENT).
Then change the onCloseTabClicked function in SuperTabBar to know about your CustomEvent. Then you can pass that to your application code (by adding it to the SuperTabEvent, perhaps).

Child mouseEnabled

There is a main CONTAINER which contains a lot of other movieclips, in these CHILD1 movieclips, there are a CUBE and an IMAGE movieclip too. I want to disable the mouse events for only the IMAGE movieclip, is it possible?
CONTAINER
-CHILD1
-CUBE //this has mouse events!
-IMAGE //want to disable the mouse events for this!
-CHILD2
-CUBE //this has mouse events!
-IMAGE //want to disable the mouse events for this!
-CHILD3
-CUBE //this has mouse events!
-IMAGE //want to disable the mouse events for this!
Any idea? Thanks!
CHILD's code chunk:
private function added(e:Event) {
removeEventListener(Event.ADDED_TO_STAGE, added);
addEventListener(MouseEvent.MOUSE_UP, NEXT);
}
public function NEXT(e:MouseEvent) {
//OB is the instance name of the IMAGE
if(e.target.name == "OB"){
e.stopImmediatePropagation();
return;
}
OB.gotoAndStop(Main.ID);
}
[SOLVED]
To disable a specific child's event listeneing:
private function added(e:Event) {
mouseEnabled = false; //This is the clue.
OB.mouseEnabled = false;
OB.mouseChildren = false;
removeEventListener(Event.ADDED_TO_STAGE, added);
cube.addEventListener(MouseEvent.MOUSE_UP, NEXT);
}
If your mouse listener is attached to the image, set the IMAGE's mouseEnabled & mouseChildren properties to false in the constructor (OR If using the timeline in Flash CSX) then on the IMAGE objects timeline (on the first frame) set mouseEnabled & mouseChildren to false.
HERE would be the changes to your posted code (not sure what you called your instance of CUBE):
private function added(e:Event) {
removeEventListener(Event.ADDED_TO_STAGE, added);
CUBE.addEventListener(MouseEvent.MOUSE_UP, NEXT);
OB.mouseEnabled = false;
OB.mouseChildren = false;
}
public function NEXT(e:MouseEvent) {
OB.gotoAndStop(Main.ID);
}
Give a instance name to your image(assuming it is "OB") and:
private function added(e:Event)
{
this.getChildByName("OB").mouseEnabled = false
this.getChildByName("OB").mouseChildren = false;
removeEventListener(Event.ADDED_TO_STAGE, added);
addEventListener(MouseEvent.MOUSE_UP, NEXT);
}
If this doesn't work, probably you have other issues in your code and you should explain and show your code.

AS3:Calling a switch case from another class does't work

i am making a game in flash and the problem that i am having is with two classes a class called Menu for the welcome screen which has a startgame button and the engine class which runs everything when i run the game for the first time i set the menu to appear automatically by this code:
public var state:int;
public const MENU:int = 0;
public const GAME:int = 1;
// create the variable for the menu
private var _menu:Menu;
public var sBg1:ScrollBg;
public var sBg2:ScrollBg;
public function Engine(menu:Menu)
{
_menu = menu;
//we add event listener when the engine is added to the stage
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded(e:Event):void {
//then we remove the event listener and initiate the init function
removeEventListener(Event.ADDED_TO_STAGE, onAdded);
init();
}
private function init():void {
//the init function set the game state at first to menu and run the drawUI function
state = MENU;
drawUI();
}
public function drawUI():void
{
//the drawUI function draw the needed elements on stage according to the state of the game
switch(state){
case MENU:
_menu = new Menu();
addChild(_menu);
break;
case GAME:
sBg1= new ScrollBg();
addChild(sBg1);
sBg1.x = 0;
sBg1.y = 0;
//if(contains(_menu)){
//removeChild(_menu);}
trace('this the game');
break;
}
}
and i change the state from menu to the actual game using this code in the menu class:
public var start_btn:MovieClip;
private var _engine:Engine;
public function Menu()
{
addEventListener(Event.ADDED_TO_STAGE, displayMenu);
}
private function displayMenu(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, displayMenu);
start_btn = new startBtn();
start_btn.x = 100;
start_btn.y = 200;
addChild(start_btn);
start_btn.addEventListener(MouseEvent.CLICK, startGame);
}
private function startGame(e:MouseEvent):void
{
start_btn.removeEventListener(MouseEvent.CLICK, startGame);
_engine = new Engine(this);
_engine.state = 1;
_engine.drawUI();
}
i use the drawUI function each time for the menu for example it places the start button and for the game it places the background but for some reason i only get the trace statement saying (this the game) but the background is not displayed any clues .
i spend more then 4 hours trying to find out what is the problem and i still can't if anyone can help me that would be great.
Thanks
If you see the trace but nothing on screen that is most likely because the ScrollBg class doesn't have anything to display. Make sure that class has some sprites or images in it.

Click event outside MovieClip in AS3

Is there any way to detect if the user click outside a MovieClip?
For instance, I need to detect it to close a previously opened menu (like Menu bar style: File, Edition, Tools, Help, etc).
How can I detect this kind of event? Thanks!
Add a listener to stage and check if stage is the target of the event.
Example of code here:
http://wonderfl.net/c/eFao
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class FlashTest extends Sprite
{
private var _menu : Sprite;
public function FlashTest()
{
_menu = new Sprite();
_menu.x = 100;
_menu.y = 100;
_menu.alpha = 0.5;
with(_menu.graphics)
{
beginFill(0xFF0000, 1);
drawRect(0, 0, 300, 300);
endFill();
}
addChild(_menu);
_menu.addEventListener(MouseEvent.CLICK, onClickHandler);
stage.addEventListener(MouseEvent.CLICK, onClickHandler);
}
private function onClickHandler(event : MouseEvent) : void
{
switch(event.target)
{
case _menu:
_menu.alpha = 0.5;
break;
case stage:
_menu.alpha = 1;
break;
}
}
}
}
You can add a listener to the click event of the root element:
MovieClip(root).addEventListener(MouseEvent.CLICK, clickObject);
then in the function clickObject, you can check to see what you are clicking.
function clickObject(e:Event):void
{
var hoverArray:Array = MovieClip(root).getObjectsUnderPoint(new Point(stage.mouseX, stage.mouseY));
var hoverOverObject:* = hoverArray[hoverArray.length - 1];
}
hoverOverObject references the element that you are clicking on. Often this will be the shape within the movie clip, so you'll need to look at it's parent then compare it to your movie clip. If the click wasn't on the drop down movie clip, trigger the close.
var container:MovieClip = new MovieClip();
var mc:MovieClip = new MovieClip();
with(mc.graphics){
beginFill(0xff0000,1);
drawCircle(0,0,30);
endFill();
}
mc.name = "my_mc";
container.addChild(mc);
addChild(container);
stage.addEventListener(MouseEvent.CLICK, action);
function action (e:MouseEvent):void
{
if(e.target.name != "my_mc"){
if(container.numChildren != 0)
{
container.removeChild(container.getChildByName("my_mc"));
}
}
}
Use capture phase:
button.addEventListener(MouseEvent.CLICK, button_mouseClickHandler);
button.stage.addEventListener(MouseEvent.CLICK, stage_mouseClickHandler, true);
//...
private function button_mouseClickHandler(event:MouseEvent):void
{
trace("Button CLICK");
}
private function stage_mouseClickHandler(event:MouseEvent):void
{
if (event.target == button)
return;
trace("Button CLICK_OUTSIDE");
}
Note that using stopPropagation() is good for one object, but failed for several. This approach works good for me.
Use a stage and a sprite (menu) click listener with the sprite listener executing first and apply the stopPropagation() method to the click handler of the sprite. Like this:
menu.addEventListener(MouseEvent.CLICK, handleMenuClick);
stage.addEventListener(MouseEvent.CLICK, handleStageClick);
private function handleMenuClick(e:MouseEvent):void{
// stop the event from propagating up to the stage
// so handleStageClick is never executed.
e.stopPropagation();
// note that stopPropagation() still allows the event
// to propagate to all children so if there are children
// within the menu overlay that need to respond to click
// events that still works.
}
private function handleStageClick(e:MouseEvent):void{
// put hide or destroy code here
}
The idea is that a mouse click anywhere creates a single MouseEvent.CLICK event that bubbles from the stage, down through all children to the target, then back up through the parents of the target to the stage. Here we interrupt this cycle when the target is the menu overlay by not allowing the event to propagate back up to the parent stage, ensuring that the handleStageClick() method is never invoked. The nice thing about this approach is that it is completely general. The stage can have many children underneath the overlay and the overlay can have its own children that can respond to clicks and it all works.

Event isn't removed from Listener in AS3

I'm developing a drag'n'clone feature for my flash app (AS3 and Flash CS5). The clones are created perfectly, but when I try to drag the clone recently created, the app creates a new clone (and allow me to drag it).
I want to remove this behaviour: clone only should be drag and dropped, not cloned. My code is:
public class Car extends MovieClip
{
// imports...
public function Car()
{
addListeners();
}
private function addListeners():void
{
this.addEventListener(MouseEvent.MOUSE_DOWN,clone);
}
private function clone(e:MouseEvent):void
{
// Clone the object
var newcar = new e.target.constructor;
newcar.graphics.copyFrom(this.graphics);
newcar.x = this.x;
newcar.y = this.y;
this.parent.addChild(newcar);
newcar.addEventListener(MouseEvent.MOUSE_MOVE,dragCar);
newcar.addEventListener(MouseEvent.MOUSE_UP,dropCar);
}
private function dragCar(e:MouseEvent):void
{
e.target.startDrag();
}
private function dropCar(e:MouseEvent):void
{
e.target.stopDrag();
// This line doesn't remove the event, and I don't know why
e.currentTarget.removeEventListener(MouseEvent.MOUSE_DOWN,clone);
e.target.removeEventListener(MouseEvent.MOUSE_MOVE, dragCar);
e.target.removeEventListener(MouseEvent.MOUSE_UP,dropCar);
}
}
I hope someone can help me. Thanks!
Here as you are creating a new instance of Car in the clone function, the constructor of that new Car object is called which in turn calls addListeners() and the clone has a MOUSE_DOWN event listener that again clones the clone. That is why you have the problem you describe.
To avoid this you need to add the following line in you clone function (this does not work see below edit)
newcar.removeEventListener(MouseEvent.MOUSE_DOWN,clone);
this removes the clone event listener from the cloned (new) Car instance and avoids cloning again.
Also to start drag you should do it in MOUSE_DOWN instead of MOUSE_MOVE.
Update
OK I see, MOUSE_DOWN is not called Again for the clone, so you need to use MOUSE_MOVE. Thus in this case I would remove the MOUSE_MOVE listener in the listener's body so that it is only called once.
Update
This seems to remove the event listener.
newcar.removeEventListener(MouseEvent.MOUSE_DOWN, newcar.clone);
You have to refer to the newcar instance's clone method newcar.clone to remove the event listener.
Working Code
The following code is working just fine
package{
import flash.display.MovieClip;
import flash.events.*;
public class Car extends MovieClip
{
public function Car()
{
addListeners();
}
private function addListeners():void
{
this.addEventListener(MouseEvent.MOUSE_DOWN,clone);
}
private function clone(e:MouseEvent):void
{
// Clone the object
var newcar = new e.target.constructor;
newcar.graphics.copyFrom(this.graphics);
newcar.x = this.x;
newcar.y = this.y;
this.parent.addChild(newcar);
// remove the clone listener
newcar.removeEventListener(MouseEvent.MOUSE_DOWN, newcar.clone);
// add dragging listeners
newcar.addEventListener(MouseEvent.MOUSE_MOVE, dragCar);
newcar.addEventListener(MouseEvent.MOUSE_UP, dropCar);
// this is used for dragging the clone
newcar.addEventListener(MouseEvent.MOUSE_DOWN, dragCar);
}
private function dragCar(e:MouseEvent):void
{
// if a MOUSE_MOVE event listener is registered remove it
if(e.target.hasEventListener(MouseEvent.MOUSE_MOVE))
e.target.removeEventListener(MouseEvent.MOUSE_MOVE, dragCar);
e.target.startDrag();
}
private function dropCar(e:MouseEvent):void
{
e.target.stopDrag();
// do not remove the listener if you want the clone to be dragable
// e.target.removeEventListener(MouseEvent.MOUSE_UP,dropCar);
}
}
}
Keep clone dragable
For this in the above code I have added a MOUSE_DOWN event listener for dragging the clone
newcar.addEventListener(MouseEvent.MOUSE_DOWN, dragCar);
And commented out the e.target.removeEventListener(MouseEvent.MOUSE_UP,dropCar); in dropCar
Also added a Check before removing MOUSE_MOVE listener as to whether it is there or not, as this function will be called later by MOUSE_DOWN also.