Events are dispatched to listeners. Event listeners can be either functions or objects (see Function vs Table Listeners below). In both cases, when an event occurs, the listener will be invoked and be supplied with a table representing the event. All events will have a property name that identifies the kind of event.
Certain objects you create using Corona's libraries are event listeners. This includes both display objects and the global Runtime object. You can add and remove listeners for events using the following object methods:
In the example below, an image display object registers to receive Touch Events. Touch events are not broadcast globally. Only objects that register for the event and lie underneath it will be candidates for receiving the touch. See Touch Events for more information.
| Function Listeners | Table Listeners |
|---|---|
local button = display.newImage("button.png") local function listener(event) print(event.name.."occurred") return true end button:addEventListener( "touch", listener ) |
local button = display.newImage("button.png") function button:touch(event) print(event.name.."occurred") return true end button:addEventListener( "touch", button ) |
In contrast, Runtime Events are dispatched by the system. They are broadcast to all listeners. Below is an example of registering for an enterFrame event:
| Function Listeners | Table Listeners |
|---|---|
local function listener(event) print(event.name.."occurred") end Runtime:addEventListener( "enterFrame", listener ) |
local listener = {} function listener:enterFrame(event) print(event.name.."occurred") end Runtime:addEventListener( "enterFrame", listener ) |
Listeners can be either functions or table/display objects.
When a function listener is invoked, it is passed a table representing the event:
1 2 3 4 5 | local myListener = function( event ) print( "Listener called with event of type " .. event.name ) end Runtime:addEventListener( "touch", myListener ) Runtime:addEventListener( "enterFrame", myListener ) |
Sometimes a function listener is not convenient because certain variables are not in scope when the listener is triggered (invoked). In these situations, object listeners should be used. Object listeners must have an instance method with a name corresponding to the event name:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | -- assume MyClass and MyClass:new() already exist function MyClass:enterFrame( event ) print( "enterFrame called at time: " .. event.time ) end function MyClass:touch( event ) print( "touch occurred at ("..event.x..","..event.y..")" ) end local myObject = MyClass:new() Runtime:addEventListener( "touch", myObject ) Runtime:addEventListener( "enterFrame", myObject ) |
Since they have no specific target, Runtime Events are only sent to the global Runtime. Instead, they are broadcast to all registered listeners. The events all have string names:
enterFrame events occur at the frame interval of the application. They are only sent to the global Runtime object.
The following properties are available in this event:
"enterFrame".System events are dispatched to notify the application of external events such as when the device needs to suspend the application because of an incoming phone call. These events are only sent to the global Runtime object.
The following properties are available in this event:
"system"."applicationStart" occurs when the application is launched and all code in main.lua is executed."applicationExit" occurs when the user quits the application."applicationSuspend" occurs when the device needs to suspend the application such as during a phone call or if the phone goes to sleep from inactivity. In the simulator, this corresponds to the simulator running in the background. During suspension, no events (not even enterFrame events) are sent to the application while suspended, so if you have code that depends on time, you should account for the time lost to an application being suspended."applicationResume" occurs when the application resumes after a suspend. On the phone, this occurs if the application was suspended because of a phone call. On the simulator, this occurs when the simulator was in the background and now is the foreground application.Orientation events occur when the device orientation changes. They only occur on devices with accelerometer support. They are only sent to the global Runtime object.
The following properties are available in this event:
"orientation"."portrait""landscapeLeft""portraitUpsideDown""landscapeRight""faceUp""faceDown"Accelerometer events let you detect sudden movements and determine the device's orientation relative to gravity. These events are only dispatched on devices that have support accelerometer. They are only sent to the global Runtime object.
The following properties are available in this event:
"accelerometer".These are location events generated by the GPS hardware. They are only sent to the global Runtime object.
"location".When an error occurs, the following properties will be non-nil:
These are heading events generated by the compass hardware, if available on the device. Note: the Android OS only supports event.magnetic, not event.geographic (see below). These events are only sent to the global Runtime object.
The following properties are available in this event:
"heading".The iOS low memory warning is exposed as a Corona event type named "memoryWarning", sent to the global Runtime object. This event has no fields.
When this event fires, the OS reserves the right to forcibly shut down the application in about five seconds (although it may or may not do so). Apple advises developers to listen for this warning, and to handle it by freeing as much memory as possible when it is received.
Here is an example of a listener for this event:
1 2 3 4 5 | local function handleLowMemory( event ) print( "memory warning received!" ) end Runtime:addEventListener( "memoryWarning", handleLowMemory ) |
Note that Android has no equivalent of this event, so currently this is an iOS-only feature of Corona.
Targeted events are not broadcast. They are sent to a single target (a function or table listener).
Completion events signal the end of an interaction. Typically these are dispatched at the end of a modal interaction such as using the camera or throwing up a native alert.
The following are common properties of this event:
"completion".Additional properties are given depending on the modal interaction (see media.playVideo, media.show, and native.showAlert).
Timer events are used in conjunction with the timer library.
The following properties are available in this event:
"timer".URL request events are dispatched to the listener registered with the native.webPopup() function. They are sent when the web popup is about to request a url and also when a url fails to load.
The following properties are available in this event:
"urlRequest".For a tutorial on how to detect user touches, please see Detecting Touches in Corona.
When the user's finger touches the screen, a hit event is generated and dispatched to display objects in the display hierarchy. Only those objects that intersect the hit location (the location of the finger on the screen) will be candidates for receiving the event.
The events propagate through these objects in a particular order. The first object in the display hierarchy to receive the event is the top-most display object that intersects the hit location; the next object is the next top-most object intersecting the hit location; and so on.
Hit events propagate until they are handled. You can stop propagation to the next object (all listeners of the current object still get the event) by telling the system that the event was handled. This boils down to making a listener return true. If at least one of the listeners of the current object returns true, event propagation ends; the next object will not get the event. If the event is still unhandled at the end of this traversal, it is broadcast as a global event to the global Runtime object.
Hit events are a hybrid of local and global events. They are dispatched to a single display object at a time, but any listener registered with that display object will receive the event.
In some situations, it is convenient to have subsequent events target the same object that handled the initial touch event (a user's finger initially touching the screen). To accomplish this, you can use a special method display.getCurrentStage():setFocus( object ). When the user's finger is lifted, you can call the method again and pass nil to restore the default behavior.
Touch events are a special kind of hit event. When a user's finger touches the screen, they are starting a sequence of touch events, each with different phases.
"touch"."began" a finger touched the screen."moved" a finger moved on the screen."ended" a finger was lifted from the screen."cancelled" the system cancelled tracking of the touch.To illustrate the documentation below, we have provided two new sample projects:
In addition, we have made minor revisions to ui.lua to support multitouch. If you activate multitouch and plan to use ui.lua, you should include this new version in your project. The new library should also be compatible with non-multitouch cases.
When multiple touch is enabled, multiple touches are sent as individual touch events, each of which will now behave like single touches even when happening simultaneously. We have also added a time property so that you can detect simultaneous touch events when writing gesture-recognition code. Finally, we have added an id property which allows you to track which touch events came from the same finger.
Note: Multitouch does not work correctly on some Android devices (e.g., NexusOne, HTC Incredible, etc.). This can be demonstrated in Drag Me Multitouch sample app. This is a limitation of the current Android platforms and not Corona.
Whether or not multitouch is enabled, touch events will have the following standard properties:
system.getTimer().system.activate( "multitouch" )
Once this call is made, you will get multiple touch events delivered as individual single touch events. For example, if two fingers moved simultaneously, Corona will hit-test each touch separately, delivering them to the respective listeners; both events will have the same timestamp.
In the single-touch world, we provided the ability to have touch events target a particular object via the stage:setFocus(object) method, which caused all future touch events to be sent directly to the touch listener for that object. This made behaviors like button rollovers possible. The typical flow, as seen in the previous ui.lua library, was:
stage:setFocus( nil ).In a multitouch world, this model breaks down, because it assumes there will only be one unique touch at a time. As a result, two buttons couldn't be pressed simultaneously.
Therefore, we had to add the ability to forward a touch event on a per-object basis. To do this, we extended the event with a parameter that uniquely identifies the event associated with a given finger touching the screen:
stage:setFocus( object [,touchID] )
Calling the above with only one parameter (the object) is the same as the old global focus behavior: all touches will be forwarded to the same object.
Calling the above method with the optional parameter (the touchID) means that the specified touch has focus on that object, but other touches do not. Using this API, it is possible to create an object that will “own” the first touch it gets, for the lifetime of that touch, and for multiple objects to obtain their own focused touches at the same time. See the “GettingStarted/FollowMeMultitouch” sample code for a demonstration of this behavior.
To turn off per-object focus, you specify the object and pass nil for the touchID:
stage:setFocus( object, nil )
Note: for backwards compatibility, stage:setFocus() reverts to the old setFocus behavior when multitouch is off:
stage:setFocus( obj, nil ) behaves like stage:setFocus( nil )stage:setFocus( obj, id ) behaves like stage:setFocus( obj )As a result, the latest ui.lua library code, distributed with Beta 6 or later, should work as expected in both multitouch and non-multitouch modes.
See the "Multitouch" sample code in the iPad sample code directory for an example of how to implement a pinch/zoom gesture.
We intend to provide additional framework libraries to make gesture recognition easier, but we encourage you to experiment with multitouch in the meantime.
In Corona, you can register custom events with both display objects and the global Runtime object. In both cases, you will have to manually dispatch the event yourself using the following object method:
Dispatches event to object. The event parameter must be a table with a name property which is a string identifying the type of event. The object parameter must be either a display object or the global Runtime object. If object has a listener registered to receive name events. We recommend you also include a target property to the event so that your listener can know which object received the event.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | -- Create an object that listens to events local image = display.newImage( "image.png" ) -- Setup listener local myListener = function( event ) print( "Event " .. event.name ) print( "Target has width: " .. event.target.stageWidth ) end image:addEventListener( "myEventType", myListener ) -- Sometime later, create an event and dispatch it local event = { name="myEventType", target=image } image:dispatchEvent( event ) |
In creating a "touch" listener, is there anyway to determine which object is the focus of the event in the listener function? For example,
table =
{
{ value=10, image=display.newImage(group1, "file1.png") },
{ value=20, image=display.newImage(group1, "file2.png") }
}
for i, v in ipairs(table) do
v.image:addEventListener("touch", UpdateScore)
end
function UpdateScore(event)
if ( event.phase == "ended" ) then
score = score + object.value --somehow update the score based on which object is receiving the event
end
end
You can determine which object was touched by using event.target. If the target has properties associated with it, such as a value or id, then you can retrieve those using event.target.value or event.target.id. Here's a simplified example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | local score = 0 myObject1 = display.newImage("file1.png", 50, 50) myObject2 = display.newImage("file2.png", 150, 150) myObject1.value = 10 myObject2.value = 20 function UpdateScore(event) local t = event.target if ( event.phase == "ended" ) then score = score + t.value --update the score print("score: ".. score) --print the score to the terminal end end myObject1:addEventListener("touch", UpdateScore) myObject2:addEventListener("touch", UpdateScore) |
Thank you! That was what I needed to know.
-Kelly
Thanks Gilbert... that code helped me a lot... thanks...
Can someone explain the difference between a touch and tap event?
Also, is there any support or sample code for a double-tap event?
Thanks!
initial sample code for a table listener gives a syntax error if the function on the table is declared as local:
should
1 2 3 4 | local function button:touch(event) print(event.name.."occurred") return true end |
1 2 3 4 | function button:touch(event) print(event.name.."occurred") return true end |
I am experiencing the same - is the sample code wrong ?
What is the impact of NOT making these event functions "local" ?
The table listeners samples on this page that are defined with "local" are wrong and shouldn't have "local". The objects have already been defined and the table listener is only adding the method to table/object. I fixed the samples.
Hi all,
I have question about using dispatchEvent to change screens using the viewController supplied with the tab bar example. I used that as my base because it fit my application ui that I envisioned quite well. However, now I am at a point where I need to automatically return to my last screen after having selected a different tab. For example: I am in screen 1 then I switch to screen 2 where I am presented with some choices and, once selected, I want to automatically return to screen 1 without having to touch the tab bar. When I try to dispatch the event my code looks like this:
local event = { name="touch", phase="ended", id=2, target = tabBar }
Runtime:dispatchEvent(event)
I thought that, perhaps I needed to pecify tabBar.tab[1] but that gives me an index error.
Admittedly, I am a noob at this but I have been programming for 30 years so I'm a little frustrated at not figuring this one out yet.
Any help would be immensely appreciated.
Hmm.. I think this documentation also include why and when a "return true" should be included on an event listener..
I have to go to a forum to have it answered. Instead of just looking on the documentation.
Cheers!
Third paragraph under "Touch Events" on this page:
"If at least one of the listeners of the current object returns true, event propagation ends; the next object will not get the event. If the event is still unhandled at the end of this traversal, it is broadcast as a global event to the global Runtime object."
I think, they were not there before. ^_^ That's why my comment. and after some weeks I saw the explanation. Its either I missed it... Or they have updated it.
anyway.. as long its there now.. its nice.
It says above in the docs that "Whether or not multitouch is enabled, touch events will have the following standard properties" and includes event.id as one of these properties... but is this really true when multitouch is not enabled? If so, why?
couldnt see anything about changing gravity with orientation.
I'm just starting out with Corona and thought I'd share this.
1 2 3 4 5 6 7 8 9 10 11 12 13 | gaOrientationGravity = { portrait = {x=0,y=-1}, portraitUpsideDown = {x=0,y=1}, landscapeLeft= {x=1,y=0}, landscapeRight = {x=-1,y=0} } local function onRotation( poEvent ) local aVector aVector = gaOrientationGravity[poEvent.type] physics.setGravity( 10 * aVector.x, -10 * aVector.y ) end Runtime:addEventListener( "orientation", onRotation ) |
Hi,
How often is a "touch" listener called if the user keeps pressing?
Is it once per frame?
If an object has event listeners, is it necessary to remove them if the object is removed from a display group?
In other words, do event listeners NEED to be tidied up?