CoronaSDK之交互系统和事件检测

事件(Event)是Corona应用程序的基础。他们用来触发不同事件对应的响应,例如触摸屏幕、检测一个特定的系统事件、定时器的完成、两个物理体的碰撞,等等。通常我们使用display object的对象方法addEventListener来添加处理函数,以关联需要监听的事件。

1 Runtime事件(似乎也可以叫做全局事件)

Runtime事件被分配给全局的Runtime监听器。这些事件并不直接和任何具体的对象相关;当然他们广播给所有感兴趣的监听器。Runtime event包括下列这些:

  • enterFrame:发生在每秒帧数间隔之间的事件(corona渲染一般每秒30或60帧)
  • system:某种由系统状况引发的事件,例如设备挂起应用或退出应用
  • orientation:当改变设备的方向,横向到竖向或者反之,而发生的事件

下面的代码展示了一个应用如何响应system事件:

local function onSystemEvent( event )

    local eventType = event.type

    if ( eventType == "applicationStart" ) then
        --occurs when the application is launched and all code in "main.lua" is executed
    elseif ( eventType == "applicationExit" ) then
        --occurs when the user or OS task manager quits the application
    elseif ( eventType == "applicationSuspend" ) then
        --perform all necessary actions for when the device suspends the application, i.e. during a phone call
    elseif ( eventType == "applicationResume" ) then
        --perform all necessary actions for when the app resumes from a suspended state
    elseif ( eventType == "applicationOpen" ) then
        --occurs when the application is asked to open a URL resource (Android and iOS only)
    end
end

Runtime:addEventListener( "system", onSystemEvent )

 

2 本地事件(local event)

本地事件被发送给一个单独的Listener,而不是广播给全局的运行时环境。一般本地事件包括下列事件:

hit:当用户触摸设备屏幕时发生的事件

collision:物理对象碰撞时发生的事件

timer:在运行timer完成时发生的事件

audio:当一个音频播放完成时分发的事件

 

3 function listener 和 table listener

Listener可以是一个function,也可以是一个table。

当你一个function Listener被调用,它会接受一个table参数,作为event。

local myListener = function( event )
    print( "Listener called with event of type: "..event.name )
end

Runtime:addEventListener( "touch", myListener )
Runtime:addEventListener( "enterFrame", myListener )

 

有时一个function Listener并不方便,因为当Listener被调用时一些变量并不在作用域里。在这种情况里,table Listener应该被使用。table Listener必须有一个有对应事件名的实例方法:

--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 )

注意:

这里是对象方法,用的是“:”,而不是“.”。所以实际上函数内部是可以访问self引用的。这里的self就是myObject的引用。

 

4 理解hit事件

当用户触摸屏幕时,一个hit事件会生成并分发给显示层次中的显示对象(display object)。只有那些在屏幕上位置和触摸点位置发生交集的对象会收到事件。hit事件会以一个特定的顺序穿过这些对象。默认情况下,第一个收到事件的对象是发生交集的对象中最上层的显示对象,往下一个发生交集的显示对象会第二个收到事件,如此类推。

hit事件会一直传递下去,除非它被处理掉。这意味着如果你有多个层叠在一起的对象,并且一个hit事件监听器被应用到所有这些对象,hit事件将会穿过着所有的对象。然而,你可以停止传递给下一个低层对象,这就需要你告诉corona这个event已经被处理了。简单的方法就是在事件处理函数直接返回一个true--这将会停止传递周期,并且阻止任何底下对象响应这个hit事件。

local function myTouchListener( event )

    if ( event.phase == "began" ) then
        --code executed when the button is touched
        print( "object touched = "..tostring(event.target) )  --'event.target' is the touched object
    end
    return true  --prevents touch propagation to underlying objects
end

local myButton = display.newRect( 100, 100, 200, 50 )
myButton:addEventListener( "touch", myTouchListener )

注意:如果hit事件遍历了整个显示层次中所有的显示对象,仍然没有被处理,它将会像全局事件一样广播给Runtime Listener。换言之,runtime listener还有机会对未命中任何对象的触摸事件做出反应。

 

4.1 设置焦点

你可以直接发送任何hit事件给一个指定的display object,来为这个对象设置焦点。比如一个典型的button,如果用户触摸这个按钮--还没有抬起手-又滑出了按钮,这个按钮一般不应该激发一个行为。同理,如果手指滑出按钮并接触到另一个交互对象,你可能不想激活那个对象,因为触摸是从按钮开始的,而不是交互对象。

解决这个问题的简单方法就如下,进入按钮时设置焦点,结束和取消时再取消焦点。这样滑出到其他对象时,其他对象都收不到事件

local function myTouchListener( event )

    if ( event.phase == "began" ) then
        display.getCurrentStage():setFocus( event.target )  --'event.target' is the touched object

    elseif ( event.phase == "ended" or event.phase == "cancelled" ) then
        display.getCurrentStage():setFocus( nil )  --setting focus to 'nil' removes the focus

    end
    return true
end

local myButton = display.newRect( 100, 100, 200, 50 )
myButton:addEventListener( "touch", myTouchListener )

 

4.2 hit event具体分为

  • Tap
  • Touch
  • Multitouch

 

5 event 参数

上面举出的每个例子,事件都会被分发给事件监听函数。一个event参数始终会被传递给监听函数。

local function myTouchListener( event )

    print( "Touch X location"..event.x )
    print( "Touch Y location"..event.y )
end

local myButton = display.newRect( 100, 100, 200, 50 )
myButton:addEventListener( "touch", myTouchListener )

注意:传递给myTouchListener函数的event参数中包含手指接触屏幕位置的x和y坐标。event参数关联的属性和event类型是一一关联的。什么事件 对应哪些属性。

 

6 注册EventListener

我们一般用对象方法addEventListener()来注册事件。只要传入事件名称字符串,以及对应处理这个事件的处理函数即可。

--standard touch listener on an object
myButton:addEventListener( "touch", myTouchListener )

--Runtime 'system' event listener
Runtime:addEventListener( "system", onSystemEvent )

重要提示:

如上所示,两种类型的事件监听器都可以通过addEventListener对象方法来创建:监听器附着到显示对象,或者监听器附着到全局Rumtime。

当你创建一个显示对象或添加一个本地的touch事件监听器 给它,你需要通过引用函数来指定一个代码块给事件。这些代码处于他们自己内存的代码段当中,并且当显示对象被删除后依然存在。不过当显示对象被删除后,就没办法检测到新的事件,因为本地事件监听器(注意:是监听器,不是代码)已经被有效的删除,并不需要你显式地删除监听器。

另一方面,Runtime事件监听器需要在你用完之后手动删除。此外,他们将继续运行,因为全局Runtime事件是全局的。不仅仅会导致内存泄漏,而且如果函数继续存在于Runtime中,并引用了不再存在的对象,程序也会崩溃。

 

7 删除Event Listener

大多数监听器(Listener),除了“enterFrame”之类的Runtime监听器,在显示对象被删除(obj:removeSelf或display.remove(obj))时将会被自动删除。但是,你也可以不删除对象,而只手动删除事件监听器。

删除(摘除)一个事件监听器,需要依靠内建的removeEventListener对象方法。

这个函数调用方式和addEventListener差不多。比如,你想让一个按钮停止监听touch事件,就可以摘除其监听器,如下:

myButton:removeEventListener( "touch", myTouchListener )

删除关联的事件监听器,需要提供事件名和函数名,因为有可能一个对象连接了同一类对象的多个监听器。例如,你可以有两个或更多监听函数关联到myButton的touch事件。然而当调用removeEventListener()时,你需要指定你想要停止监听的事件的类型,以及之前关联到这个事件上的函数。

posted @ 2015-01-21 13:31  弯弓射月  阅读(1829)  评论(1编辑  收藏  举报