I have a :
Screen implementing InputProcessor
A stage created in the screen.
an Actor added to the stage
the Actor has a InputListener with a touchUp method, which is called when the actor is touched.
When clicking on the actor the method touchUp of the actors inputlistener is called and the touchUp from the InputProcessor is also called.
Question:
How can I prevent calling the touchup method from the screen ? My goal is only to handle the event from actor. I would like to only react on the touch from the actor and not also from the screen.
Here's a references to help you better understand my answer:
https://libgdx.badlogicgames.com/ci/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputListener.html#touchDown-com.badlogic.gdx.scenes.scene2d.InputEvent-float-float-int-int-
That link shows one of the methods in the InputListener class. In your question, you asked about the touchUp method which is also related to the touchDown method.
The touchDown method returns a boolean while touchUp is void. Because of this, you can't cancel a "touch up" event. However, you can "cancel" a "touch down" event by returning true. I say "cancel" because it's not really cancelling it, it's just letting the implementation know that you are handling the "touch down" and "touch up" events now.
Using the touchDown method is probably your best option. I believe you will still be able to use touchUp but I am unaware if other listeners receive the touchUp call if touchDown returned true (Given the documentation, I don't think they will). That's something you'll have to test for yourself.
Now if you really need cancel a "touch up" event, do this:
https://libgdx.badlogicgames.com/ci/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/Event.html#cancel--
You can try to cancel the event. Because the touchUp method in InputListener passes an InputEvent object, you can try to cancel it using inputEvent.cancel()
If you will catch Events like touchUp, touchDown etc. you must say that the event is handled otherwise the event will run to the next InputListener until one Listener says that the event is handled.
To set the Event to handled you must call the handle() method on the event object:
actor.addListener(new InputListener(){
#Override
public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
event.handle();
}
});
Here it is described a little bit more with InputMultiplexer: Screen responds when button is clicked
Related
I want do to the next one in flash with ActionScript 3.0:
Global event (if user click in any part of the screen by mouse):
addEventListener (MouseEvent.CLICK, nextc);
function nextc (event:MouseEvent): void
{nextFrame();}
Button event (if user click exact this button):
returnb54.addEventListener(MouseEvent.CLICK, returnb54a);
function returnb54a(event:MouseEvent):void
{prevFrame();}
But on the frame with this a global event and a button nothing happens when clicking the button.
Is there any way to prioritize button event over global?
Thank you.
I created a very simple application to test your question (I'm using the same names as you defined, to be easier to understand).
I have changed 3 points:
1-
this.stage.addEventListener (MouseEvent.CLICK, nextc);
2-
function returnb54a(event:MouseEvent):void
{
event.stopImmediatePropagation();
prevFrame();
}
3-
function nextc(event:MouseEvent): void
{
event.stopImmediatePropagation();
nextFrame();
}
The method: stopImmediatePropagation() prevents processing of any event listeners in the current node and any subsequent nodes in the event flow. This method takes effect immediately, and it affects event listeners in the current node. In contrast, the stopPropagation() method doesn't take effect until all the event listeners in the current node finish processing.
Try to implement these changes and see if will have the desired result.
I feel the way inner components could be accessed be better than what I know.
I know we can access any inner component something like:
FlexGlobals.topLevelApplication.someChild1.someChild2.someChild3… and so on
I've a components which has number of parent components hierarchy. I wish to know if there could be anyway I could access the last child without referring all of its parent.
I need to trigger an event on that component.
FlexGlobals.topLevelApplication.child1.child2.child3.dispatchEvent(new Event('clearData', true));
Updated: I tried the way you suggested in point 1. I've added the event listener on child component and try to dispatch it from a action script file, but it went unheared.\
child3.addEventLisener('clearData', clearHandler);
And then I dispatched the event some thing like:
dispatchEvent(new Event(modelApp.CLEAR_PALETTE, true)
try to add
FlexGlobals.topLevelApplication.systemManger.addEventLisener('clearData', clearHandler);
in child3
and dispacthed it from any where you want,
for dispatching use the following
FlexGlobals.topLevelApplication.systemManager.dispatchEvent(new Event(modelApp.CLEAR_PALETTE, true)
How I understood from the question used no good principles of application design, but that's another question.
In your case, you can use stage as global currentTarget for bubbles events. Dispatch event from displayObject with bubbles = true; In target display object listen event with stage object with/out capture phase.
in any displayObject:
dispatchEvent(new Event("myEvent", true));
in target displayObject:
// add ADDED_TO_STAGE event listener in constructor or when component creation complete
protected function component_creationCompleteHandler(event:FlexEvent):void
{
addEventListener(Event.ADDED_TO_STAGE, onAddToStage);
}
..
protected function onAddToStage(event:Event):void
{
stage.addEventListener("myEvent", eventHandler, true);
}
protected function eventHandler(event:Event):void
{
trace("eventHandler");
}
It is better than use reference to FlexGlobals object. Code more flexible.
I think you should go down to the level of component and addEventListener on initialize.
and dispatch from where ever you want.
other way is to do that, you need to save component reference(ID) in object on creationComplete. save its refence in your model. now you can access it where ever you want. and do what ever you want.
i also did in this way, i saved my screen components refrence in an array and access it where i need to dynamically validate all components.
otherwise you need nested looping to got your required child.
I'm working on something similar to the Angry Birds "rollout" for options, etc., but I'm running into a fairly substantial problem.
The rollout itself is just a toggle button, with several other buttons added to the display list that move when you touch the toggle button. Each of these buttons is a class that extends Sprite and contains individual methods for touch events, begin, end and out. When these buttons get initialized (NOT instantiated), the touch begin listener is added. Something like this:
public function Initialize():void
{
this.addEventListener(TouchEvent.TOUCH_BEGIN, OnTouchBegin, false, int.MAX_VALUE);
}
private function OnTouchBegin(e:TouchEvent):void
{
this.removeEventListener(TouchEvent.TOUCH_BEGIN, OnTouchBegin);
this.addEventListener(TouchEvent.TOUCH_END, OnTouchRelease, false, int.MAX_VALUE);
this.addEventListener(TouchEvent.TOUCH_OUT, OnTouchOut, false, int.MAX_VALUE);
}
private function OnTouchRelease(e:TouchEvent):void
{
this.addEventListener(TouchEvent.TOUCH_BEGIN, OnTouchBegin, false, int.MAX_VALUE);
this.removeEventListener(TouchEvent.TOUCH_END, OnTouchRelease);
this.removeEventListener(TouchEvent.TOUCH_OUT, OnTouchOut);
}
private function OnTouchOut(e:TouchEvent):void
{
this.addEventListener(TouchEvent.TOUCH_BEGIN, OnTouchBegin, false, int.MAX_VALUE);
this.removeEventListener(TouchEvent.TOUCH_END, OnTouchRelease);
this.removeEventListener(TouchEvent.TOUCH_OUT, OnTouchOut);
}
Then, when these buttons get hidden from the screen, a method is called to remove any of the listeners that are currently active on them:
public function Deactivate():void
{
this.removeEventListener(TouchEvent.TOUCH_OUT, OnTouchOut);
this.removeEventListener(TouchEvent.TOUCH_END, OnTouchRelease);
this.removeEventListener(TouchEvent.TOUCH_BEGIN, OnTouchBegin);
}
This is just for the standard button functionality (up/down texture and sound), on top of this, when I make the game, in my rollout class, I have an additional method that will add another event listener for custom logic that should occur when the button is touched (the button itself is created elsewhere).
public function AddRolloutButton(listener:Function):void
{
if (listener != null)
{
_buttons[index].addEventListener(TouchEvent.TOUCH_BEGIN, listener);
}
The buttons in the rollout itself are removed from the display list until they are to be shown. When the rollout is closed, the buttons are deactivated (removed from display list and the 3 button listeners within the button class are removed).
Everything works perfectly fine the very first time I open and close the rollout. After that, the event dispatching system just inexplicably dies. Every single InteractiveObject on the screen, regardless of position or type, becomes unusable. I traced out whether or not the listeners were still there on the rollout toggle button, and it was. I also confirmed that the rollout button itself was the only thing on the display list.
What I've noticed is that if I comment out the listener removal in the deactivate method of the button for the touch begin listener, or pass in null for the listener method in the AddRolloutButton method, everything works just fine. The issue seems to stem from having multiple listeners of the same type on the rollout buttons, and then removing one or all of them.
If anyone has any ideas of just what is going on, that would be very helpful. I was under the impression that adding multiple listeners of the same type to an InteractiveObject was perfectly valid.
UPDATE:
It appears that only TouchEvents get broken by this issue I'm having. I just tried using a mouse click listener on the stage after opening and closing the rollout, and that still works. So, only touch events are getting broken, if that helps at all.
Seems like there is something going wrong in your AddRolloutButton method. Are you sure you are assigning the correct listener function?
In your example the listener:Function argument should be equal to _buttons[index]. OnTouchBegin.
Or, since whatever class that owns the AddRolloutButton method seems to be in control of the buttons, you could potentially scrap the listener argument altogether since you know what method that needs to be triggered.
For example like this:
public function AddRolloutButton():void {
var currentButton:MyButtonClass = _buttons[index] as MyButtonClass;
currentButton.addEventListener(TouchEvent.TOUCH_BEGIN, currentButton.OnTouchBegin);
[...]
}
However, what you could do is to never remove the TouchEvent.TOUCH_BEGIN in the Deactivate function. The touch events will never be triggered when the DisplayObject isn't on the Display List. This means you don't have to worry about adding the listener every time you want to add the button to the Display List again.
If you set the listener to use a weak reference it will not hinder the button from being garbage collected when it's not needed anymore. To make your event listener use a weak reference, set the fifth argument of the addEventListener method to true.
addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
public function Initialize():void {
this.addEventListener(TouchEvent.TOUCH_BEGIN, OnTouchBegin, false, int.MAX_VALUE, true);
}
Do you call the Initialize method again at some point after closing the rollout?
From your code it looks as though you remove all of the event listeners in Deactivate and then never add the TouchEvent.TOUCH_BEGIN listener again.
I'm not sure what you're trying to accomplish by removing the event listeners. Since the Button has a reference to the callback, it has a reference to that callback whether the listeners are attached or not. Are you thinking that adding a listener creates a reference from the callback to the button? It doesn't--the reverse is true.
If you really want to have the Button release the callback when it is removed from the display list, then it can't hold that reference after it removes the listeners. Consider using something like Supervising Presenter or a RobotLegs Mediator to manage those dependencies.
I seriously doubt that the entire event system is being borked by what you're doing.
I'd be more inclined to believe that this:
if (listener != null)
{
_buttons[index].addEventListener(TouchEvent.TOUCH_BEGIN, listener);
}
is failing, either because the button that's referenced is not the one that's actually on stage, or because listener is null..
I have seen times where adding and removing events with priorities can cause things to fail, but typically this is with events that aren't propagating on the display list.
One way that you can test this is simply to listen to the main document or the stage for touch events and see if you're getting those events. If the entire event system is borked, you won't get them, but if your listener logic is wrong, you will.
You may also want to check the willTrigger property on the button and event.
I found something strange while I was coding today. I couldnot find explenation of what happend to me today.
I have created button and addEventListener to it.
private function drawButtons():void{
survivorModeBtn = new SurvivorModeBtn();
trace("I registered Event Listener with button here");
survivorModeBtn.addEventListener(MouseEvent.CLICK,onSurvivorModeBtnClick);
addChild(survivorModeBtn);
}
private function onSurvivorModeBtnClick(e:MouseEvent):void{
trace("I clicked on btn here");
//I will dispatch event here which runs second part of code
mainMenuViewEvent = new MainMenuViewEvent("onSurvivorModeIsClicked");
hideButtons();
dispatchEvent(mainMenuViewEvent);
}
Dispatched event will run those parts of code:
public function createClickToStart():void{
trace("here I register event listener MouseEvent.Click");
stage.addEventListener(MouseEvent.CLICK,onClick);
}
private function onClick(e:MouseEvent):void{
trace("onClick()");
}
When I run my program and Click on first button. I got this output in console:
I registered Event Listener with button here
I clicked on btn here
here I register event listener MouseEvent.Click
onClick()
I can t understand how can I see "onClick()" msg, if I clicked just on first button, and even in that time was not stage redistered with CLICK event.
Thank you for solution to my problem.
You have experienced what is called "bubbling" of an event. First, the "click" event is dispatched to the topmost object under cursor, then it raised upwards via parent references all the way to stage, if you don't force it to e.stopPropagation(). So, while the event was being parsed within survivorModeBtn, it has not yet reached stage, an in the meantime you ssign stage listener for this type of events. Then it bubbles up and hits stage, stage says "Wow, I have a MouseEvent.CLICK listener registered, let's call it right now!" and calls onClick() function, making you receive your last trace result.
I am coding a drag and drop application where I can grab an object and then place it over different object containers. When I am dragging the object (keeping the mouse button pressed) and I leave the stage, I can still control the object with the mouse...this is not what I want.
I would like to lose control of the object when the mouse leaves the stage.
I tried to remove the event listener for MOUSE_DOWN on a MOUSE_LEAVE event but nothing.
I also tried to dispatch a MOUSE_UP event on a MOUSE_LEAVE event but it does not work either...it works only if I manually release the mouse button.
Is there any way to override the MOUSE_DOWN event when the user moves the mouse away from the screen but he is still pressing the mouse button???
Any suggestion???
Thanks in advance
Is the stage actually listening to the MOUSE_LEAVE event? In any case , check this article, it may help:
http://www.kirupa.com/developer/flashcs3/detecting_when_mouse_leaves_movie.htm
Here's a couple tricky traps not to fall into :
One bizarre thing is that in Chrome + Firefox, the MOUSE_LEAVE event isn't dispatched for a WMODE of OPAQUE orTRANSPARENT. It just doesn't fire - mouse down or up.
With WINDOW it works fine. That one took me a long time to find out! grr... http://bugs.adobe.com/jira/browse/FP-892
Second, make sure you're using Event for the parameter type for your Event.MOUSE_LEAVE handler and not MouseEvent. If you try to handle MOUSE_LEAVE with e:MouseEvent you'll get an error that you may never see (unless you're using the debug flash player). It's a very easy mistake to make because you're probably pointing all your other handlers to the same method.
Here's what I do: (just call my main endDrag from mouseLeave(e:Event)
stage.addEventListener(MouseEvent.MOUSE_MOVE, drag);
stage.addEventListener(MouseEvent.MOUSE_UP, endDrag);
stage.addEventListener(Event.DEACTIVATE, endDrag);
stage.addEventListener(Event.MOUSE_LEAVE, mouseLeave);
private function mouseLeave(e:Event):void
{
endDrag(new MouseEvent("MOUSE_LEAVE"));
}
public function endDrag(evt:MouseEvent):void
{
/// handle end drag
}
I believe you are talking about the user leaving the flash content altogether with mouse clicked, and when he/she returns it continues the process right?
I suggest, you track the mouse x & y coordinates. Set a condition which triggers the mouse up event handler when the x or y is equal to the stage width or height respectively.
When you drag an object outside the flash movie, the object will fire a MOUSE_OUT event. You can listen to this event, use a variable to check if the object is being dragged, and, if so, dispatch a MOUSE_UP event.
some_object.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
some_object.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
some_object.addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
private function mouseOutHandler(e:MouseEvent):void
{
if (isDragging)
e.target.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_UP));
}
private function mouseDownHandler(e:MouseEvent):void
{
e.target.startDrag();
isDragging = true;
}
private function mouseUpHandler(e:MouseEvent):void
{
e.target.stopDrag();
isDragging = false;
}