下面就以我们常用的单点触摸监听来讲一下事件监听机制原理:
实现触摸监听代码如下:
local listener = cc.EventListenerTouchOneByOne:create()
local function onTouchBegan(touch, event)
return true
end
local function onTouchMoved(touch,event)
end
local function onTouchEnded(touch,event)
end
listener:registerScriptHandler(onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN)
listener:registerScriptHandler(onTouchMoved,cc.Handler.EVENT_TOUCH_MOVED)
listener:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCH_ENDED)
listener:setSwallowTouches(true)
local eventDispatcher = self:getEventDispatcher()
eventDispatcher:addEventListenerWithSceneGraphPriority(listener, self)
只要实现以上代码,在触摸屏幕的时候,onTouchBegan、onTouchMoved、onTouchEnded这几个方法就能被回调,那么具体的原理是什么呢?下面来看一下。
1、绑定listener和node
整个程序只有一个EventDispatcher,保存在Director里。通过eventDispatcher:addEventListenerWithSceneGraphPriority(listener, self),listener和被监听的节点(node)会被关联保存在一个Map【std::unordered_map<Node*, std::vector<EventListener*>*> _nodeListenersMap】,node和listener是一对多的关系。
void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)
{
CCASSERT(listener && node, "Invalid parameters.");
CCASSERT(!listener->isRegistered(), "The listener has been registered.");
if (!listener->checkAvailable())
return;
listener->setAssociatedNode(node);
listener->setFixedPriority(0);
listener->setRegistered(true);
addEventListener(listener);
}
注意:
每个监听器(Listener)和节点绑定后都会有个被注册标记,同一个监听器不能多次注册;
上面代码的最后一句将监听器都被保存在一个Map容器里面,以监听器ID为key, 该类型的所有监听器为value。结构如下:
{
"__cc_touch_all_at_once" = {ListenerA, ListenerB},
"__cc_keyboard" = {ListenerC, ListenerD}
};
2、分发事件
当产生触摸事件时,系统调用void EventDispatcher::dispatchEvent(Event* event),根据事件类型调用void EventDispatcher::dispatchTouchEvent(EventTouch* event)分发触摸事件
void EventDispatcher::dispatchEvent(Event* event)
{
if (!_isEnabled)
return;
updateDirtyFlagForSceneGraph();
DispatchGuard guard(_inDispatch);
if (event->getType() == Event::Type::TOUCH)
{
dispatchTouchEvent(static_cast<EventTouch*>(event));
return;
}
// ---------省略以下内容----
}
3、回调函数
最后查找所有的触摸监听器,回调所有符合要求的监听器dispatchEventToListeners