@class EventDispatcher
@brief This class manages event listener subscriptions and event dispatching.
首先上一段EventDispatcher的官方注释,解释了EventDispatcher的作用是管理listener和做事件分发.
进一步理解,EventDispatcher主要负责存储Listener以及当发生event时,把event发给监听此类event的listener并且多个listener监听相同event的时候,dispatcher来决定先后顺序.EventDispatcher的数据成员和其作用如下:
class CC_DLL EventDispatcher : public Ref
{
//函数已省略
protected:
//存储listeners的map容器
std::unordered_map<EventListener::ListenerID, EventListenerVector*> _listenerMap;
//脏标志map容器,用于判断Listener是否需要重新排序
std::unordered_map<EventListener::ListenerID, DirtyFlag> _priorityDirtyFlagMap;
//存储场景层监听器与它们的node
std::unordered_map<Node*, std::vector<EventListener*>*> _nodeListenersMap;
/** The map of node and its event priority */
std::unordered_map<Node*, int> _nodePriorityMap;
/** key: Global Z Order, value: Sorted Nodes */
std::unordered_map<float, std::vector<Node*>> _globalZOrderNodeMap;
/** The listeners to be added after dispatching event */
std::vector<EventListener*> _toAddedListeners;
/** The listeners to be removed after dispatching event */
std::vector<EventListener*> _toRemovedListeners;
/** The nodes were associated with scene graph based priority listeners */
std::set<Node*> _dirtyNodes;
//分发器否正在分发事件的数量:0没有事件分发,大于0有事件分发
int _inDispatch;
//分发器是否工作
bool _isEnabled;
int _nodePriorityIndex;
std::set<std::string> _internalCustomListenerIDs;
};
通过EventDispatcher的头文件可知,其成员变量主要以一些容器为主,其中_listenerMap是最重要的map,用来存储各种EventListener。再来看
EventListenerVector,它是EventDispatcher内部的一个类:
class EventListenerVector
{
public:
EventListenerVector();
~EventListenerVector();
size_t size() const;
bool empty() const;
void push_back(EventListener* item);
void clearSceneGraphListeners();
void clearFixedListeners();
void clear();
inline std::vector<EventListener*>* getFixedPriorityListeners() const { return _fixedListeners; };
inline std::vector<EventListener*>* getSceneGraphPriorityListeners() const { return _sceneGraphListeners; };
inline ssize_t getGt0Index() const { return _gt0Index; };
inline void setGt0Index(ssize_t index) { _gt0Index = index; };
private:
std::vector<EventListener*>* _fixedListeners;
std::vector<EventListener*>* _sceneGraphListeners;
ssize_t _gt0Index;
};
受EventDispatcher管理的Listener根据其优先级是否为0,被存储到_fixedListeners和_sceneGraphListeners两个向量容器内.
接下来通过跟踪源码看一下当在EventDispatcher中加入一个EventListener后,发生了什么.
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);//
}
void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)
{
CCASSERT(listener, "Invalid parameters.");
CCASSERT(!listener->isRegistered(), "The listener has been registered.");//检测一个监听器只能注册一次
CCASSERT(fixedPriority != 0, "0 priority is forbidden for fixed priority since it's used for scene graph based priority.");
if (!listener->checkAvailable())//检测监听器合法性
return;
listener->setAssociatedNode(nullptr);//设置监听器相关联节点
listener->setFixedPriority(fixedPriority);//设置监听器优先级
listener->setRegistered(true);//设置监听器注册状态
listener->setPaused(false);//固定优先级监听器无法被暂停
addEventListener(listener);
}
void EventDispatcher::addEventListener(EventListener* listener)
{
if (_inDispatch == 0)//如果EventDispatcher处于空闲状态
{
forceAddEventListener(listener);//执行常规添加操作
}
else //如果EventDispatcher正在分发事件
{
_toAddedListeners.push_back(listener);//将listener放入向量容器中
}
listener->retain();
}
void EventDispatcher::forceAddEventListener(EventListener* listener)
{
//1.添加listener
EventListenerVector* listeners = nullptr;
EventListener::ListenerID listenerID = listener->getListenerID();//一类listener的ID都一样
auto itr = _listenerMap.find(listenerID);//查找该类listener
if (itr == _listenerMap.end())//如果没找到,则在map中添加新的pair
{
listeners = new (std::nothrow) EventListenerVector();
_listenerMap.insert(std::make_pair(listenerID, listeners));
}
else//找到该类listener就获取保存listener的EventListenerVector
{
listeners = itr->second;
}
listeners->push_back(listener);
//2.设置脏标志(用于排序)
if (listener->getFixedPriority() == 0)
{
setDirty(listenerID, DirtyFlag::SCENE_GRAPH_PRIORITY);
auto node = listener->getAssociatedNode();
CCASSERT(node != nullptr, "Invalid scene graph priority!");
//3.将场景层listener和node关联起来
associateNodeAndEventListener(node, listener);//维护_nodeListenersMap容器
if (node->isRunning())
{
resumeEventListenersForTarget(node);
}
}
else
{
setDirty(listenerID, DirtyFlag::FIXED_PRIORITY);
}
}
最后看下核心函数dispatchEvent,该函数每帧都会执行
void EventDispatcher::dispatchEvent(Event* event)
{
if (!_isEnabled)
return;
updateDirtyFlagForSceneGraph();
DispatchGuard guard(_inDispatch);//更改_inDispatch状态,执行后变为1
if (event->getType() == Event::Type::TOUCH)//触摸事件单独处理
{
dispatchTouchEvent(static_cast<EventTouch*>(event));
return;
}
auto listenerID = __getListenerID(event);//通过event类型获取listenerID
sortEventListeners(listenerID);//对EventListener排序
auto pfnDispatchEventToListeners = &EventDispatcher::dispatchEventToListeners;//获取分发监听器函数的指针
if (event->getType() == Event::Type::MOUSE) {//鼠标事件的分发指针和触屏一样
pfnDispatchEventToListeners = &EventDispatcher::dispatchTouchEventToListeners;
}
auto iter = _listenerMap.find(listenerID);
if (iter != _listenerMap.end())
{
auto listeners = iter->second;
auto onEvent = [&event](EventListener* listener) -> bool{//每个响应事件的listener执行的lambda
event->setCurrentTarget(listener->getAssociatedNode());
listener->_onEvent(event);//监听器执行自身的响应函数
return event->isStopped();
};
(this->*pfnDispatchEventToListeners)(listeners, onEvent);//执行事件分发
}
updateListeners(event);//更新Listener
}
void EventDispatcher::dispatchTouchEvent(EventTouch* event)//分发触摸事件
{
sortEventListeners(EventListenerTouchOneByOne::LISTENER_ID);//单点触摸监听器排序
sortEventListeners(EventListenerTouchAllAtOnce::LISTENER_ID);//多点触摸监听器排序
auto oneByOneListeners = getListeners(EventListenerTouchOneByOne::LISTENER_ID);
auto allAtOnceListeners = getListeners(EventListenerTouchAllAtOnce::LISTENER_ID);
//如果没有touch listeners, 直接返回
if (nullptr == oneByOneListeners && nullptr == allAtOnceListeners)
return;
bool isNeedsMutableSet = (oneByOneListeners && allAtOnceListeners);
const std::vector<Touch*>& originalTouches = event->getTouches();//获取触屏坐标
std::vector<Touch*> mutableTouches(originalTouches.size());//用于多点触摸
std::copy(originalTouches.begin(), originalTouches.end(), mutableTouches.begin());//多点触摸获取数据
// process the target handlers 1st
if (oneByOneListeners)
{
auto mutableTouchesIter = mutableTouches.begin();
auto touchesIter = originalTouches.begin();
for (; touchesIter != originalTouches.end(); ++touchesIter)// 单点触摸只有一个Touch坐标
{
bool isSwallowed = false;
auto onTouchEvent = [&](EventListener* l) -> bool { // 被分发事件的监听器执行的lambda
EventListenerTouchOneByOne* listener = static_cast<EventListenerTouchOneByOne*>(l);
// Skip if the listener was removed.
if (!listener->_isRegistered)
return false;
event->setCurrentTarget(listener->_node);
bool isClaimed = false;
std::vector<Touch*>::iterator removedIter;
EventTouch::EventCode eventCode = event->getEventCode();//eventCode用来对触摸事件进行细分
//根据eventCode为BEGAN,MOVED,ENDED,CANCELLED 4种情况,调用不同的callback
if (eventCode == EventTouch::EventCode::BEGAN)
{
if (listener->onTouchBegan)
{
isClaimed = listener->onTouchBegan(*touchesIter, event);
if (isClaimed && listener->_isRegistered)
{
listener->_claimedTouches.push_back(*touchesIter);
}
}
}
else if (listener->_claimedTouches.size() > 0
&& ((removedIter = std::find(listener->_claimedTouches.begin(), listener->_claimedTouches.end(), *touchesIter)) != listener->_claimedTouches.end()))
{
isClaimed = true;
switch (eventCode)
{
case EventTouch::EventCode::MOVED:
if (listener->onTouchMoved)
{
listener->onTouchMoved(*touchesIter, event);
}
break;
case EventTouch::EventCode::ENDED:
if (listener->onTouchEnded)
{
listener->onTouchEnded(*touchesIter, event);
}
if (listener->_isRegistered)
{
listener->_claimedTouches.erase(removedIter);
}
break;
case EventTouch::EventCode::CANCELLED:
if (listener->onTouchCancelled)
{
listener->onTouchCancelled(*touchesIter, event);
}
if (listener->_isRegistered)
{
listener->_claimedTouches.erase(removedIter);
}
break;
default:
CCASSERT(false, "The eventcode is invalid.");
break;
}
}
// If the event was stopped, return directly.
if (event->isStopped())
{
updateListeners(event);
return true;
}
CCASSERT((*touchesIter)->getID() == (*mutableTouchesIter)->getID(),
"touchesIter ID should be equal to mutableTouchesIter's ID.");
if (isClaimed && listener->_isRegistered && listener->_needSwallow)
{
if (isNeedsMutableSet)
{
mutableTouchesIter = mutableTouches.erase(mutableTouchesIter);
isSwallowed = true;
}
return true;
}
return false;
};//end of lambda
dispatchTouchEventToListeners(oneByOneListeners, onTouchEvent);//单点触摸事件分给监听器
if (event->isStopped())
{
return;
}
if (!isSwallowed)
++mutableTouchesIter;
}//end of for
}//end of if
//多点触摸
if (allAtOnceListeners && mutableTouches.size() > 0)
{
auto onTouchesEvent = [&](EventListener* l) -> bool {
EventListenerTouchAllAtOnce* listener = static_cast<EventListenerTouchAllAtOnce*>(l);
// Skip if the listener was removed.
if (!listener->_isRegistered)
return false;
event->setCurrentTarget(listener->_node);
switch (event->getEventCode())
{
case EventTouch::EventCode::BEGAN:
if (listener->onTouchesBegan)
{
listener->onTouchesBegan(mutableTouches, event);
}
break;
case EventTouch::EventCode::MOVED:
if (listener->onTouchesMoved)
{
listener->onTouchesMoved(mutableTouches, event);
}
break;
case EventTouch::EventCode::ENDED:
if (listener->onTouchesEnded)
{
listener->onTouchesEnded(mutableTouches, event);
}
break;
case EventTouch::EventCode::CANCELLED:
if (listener->onTouchesCancelled)
{
listener->onTouchesCancelled(mutableTouches, event);
}
break;
default:
CCASSERT(false, "The eventcode is invalid.");
break;
}
// If the event was stopped, return directly.
if (event->isStopped())
{
updateListeners(event);
return true;
}
return false;
};
dispatchTouchEventToListeners(allAtOnceListeners, onTouchesEvent);
if (event->isStopped())
{
return;
}
}
updateListeners(event);
}
实际分发触摸和鼠标事件的函数是dispatchTouchEventToListeners,其他类型事件是dispatchEventToListeners,两者实现很像,这里只看前者源码.
void EventDispatcher::dispatchTouchEventToListeners(EventListenerVector* listeners, const std::function<bool(EventListener*)>& onEvent)
{
bool shouldStopPropagation = false;
auto fixedPriorityListeners = listeners->getFixedPriorityListeners();//获得fixPriority的vector
auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners();//获得sceneGraphPriority的vector
ssize_t i = 0;
if (fixedPriorityListeners)
{
CCASSERT(listeners->getGt0Index() <= static_cast<ssize_t>(fixedPriorityListeners->size()), "Out of range exception!");
if (!fixedPriorityListeners->empty())
{
for (; i < listeners->getGt0Index(); ++i)//只遍历优先级小于0的监听器
{
auto l = fixedPriorityListeners->at(i);//遍历fixedPriorityListeners[0]-fixedPriorityListeners[listeners->getGt0Index()-1],listeners->getGt0Index()在排序中维护
if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))//执行事件回调函数
{
shouldStopPropagation = true;
break;
}
}
}
}
auto scene = Director::getInstance()->getRunningScene();
if (scene && sceneGraphPriorityListeners)
{
if (!shouldStopPropagation)
{
// priority == 0, scene graph priority
// first, get all enabled, unPaused and registered listeners
std::vector<EventListener*> sceneListeners;
for (auto& l : *sceneGraphPriorityListeners)
{
if (l->isEnabled() && !l->isPaused() && l->isRegistered())
{
sceneListeners.push_back(l);
}
}
// second, for all camera call all listeners
// get a copy of cameras, prevent it's been modified in listener callback
// if camera's depth is greater, process it earlier
auto cameras = scene->getCameras();
Camera* camera;
for (int j = int(cameras.size()) - 1; j >= 0; --j)
{
camera = cameras[j];
if (camera->isVisible() == false)
{
continue;
}
Camera::_visitingCamera = camera;
auto cameraFlag = (unsigned short)camera->getCameraFlag();
for (auto& l : sceneListeners)
{
if (nullptr == l->getAssociatedNode() || 0 == (l->getAssociatedNode()->getCameraMask() & cameraFlag))
{
continue;
}
if (onEvent(l))
{
shouldStopPropagation = true;
break;
}
}
if (shouldStopPropagation)
{
break;
}
}
Camera::_visitingCamera = nullptr;
}
}
if (fixedPriorityListeners)
{
if (!shouldStopPropagation)
{
ssize_t size = fixedPriorityListeners->size();
for (; i < size; ++i)//只遍历优先级大于0的监听器
{
auto l = fixedPriorityListeners->at(i);//遍历fixedPriorityListeners[listeners->getGt0Index()]-fixedPriorityListeners->size()-1,listeners->getGt0Index()在排序中维护
if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
{
shouldStopPropagation = true;
break;
}
}
}
}
}
最后考虑下如果在监听响应函数中,再添加监听器并设置响应函数,那么是怎么处理?
这种情况新加的监听器会在下次触发事件时起作用,再来看下addEventListener和updateListeners(event)两个函数:
void EventDispatcher::addEventListener(EventListener* listener)
{
if (_inDispatch == 0)
{
forceAddEventListener(listener);//EventDispatcher空闲,常规添加listener
}
else
{
_toAddedListeners.push_back(listener);//EventDispatcher繁忙,将在updateListeners(event)中添加listener
}
listener->retain();
}
void EventDispatcher::updateListeners(Event* event)
{
CCASSERT(_inDispatch > 0, "If program goes here, there should be event in dispatch.");
if (_inDispatch > 1)
return;
//中间省略
if (!_toAddedListeners.empty())
{
for (auto& listener : _toAddedListeners)
{
forceAddEventListener(listener);//常规添加listener
}
_toAddedListeners.clear();
}
if (!_toRemovedListeners.empty())
{
cleanToRemovedListeners();//常规删除listener
}
}
dispatchEvent总结:
1.调用sortEventListeners对listener排序
2.调用dispatchTouchEventToListeners并传listenervVector和回调onTouchEvent给它
3.按照监听器优先级小于0,优先级==0,优先级>0的顺序执行回调函数
4.调用updateListeners(event);
另外有几个注意事项:
1.当TouchEvent Began来了之后,所有的listener会依次响应Touch Began,然后再依次响应Touch Move,而不是一个listener响应完 began move end之后 轮到下一个listener响应的顺序。
2.吞噬操作只有发生在began return true后才可以发生
3.FixedPriority listener添加完之后需要手动remove,而SceneGraphPriority listener是跟Node绑定的,在Node的析构函数中会被移除。
面试环节
问:介绍下cocos2d-x事件机制
答:和事件相关最重要的3个类为Event,EventListener和EventDispatcher。
Event类定义了事件的种类,如触摸,鼠标,键盘,加速度等。
EventListener最主要的任务是保存事件的回调函数,EventListener中有个优先级变量,根据该值是否为0将EventListener分为两类,优先级为0的为场景监听器,非0为固定监听器。另外优先级会被用来判断监听器中回调函数执行先后顺序,优先级越低的监听器越先接到事件。
第三个类EventDispatcher的作用是管理listener和做事件分发。每一帧的drawScene中会调用pollEvent来检测事件,pollEvent中执行dispatchEvent;dispatchEvent首先会对listener排序,然后按照监听器优先级小于0,等于0,大于0的顺序执行回调函数,最后更新监听器。