-
Notifications
You must be signed in to change notification settings - Fork 43
cocos2d x 3.3 012 事件分发机制
cheyiliu edited this page Jan 30, 2015
·
18 revisions
- 什么是事件? Event及子类EventXXX,核心成员_type
- 谁监听事件? EventListener及子类EventListenerXXX,核心成员_onEvent
- 谁派发事件? EventDispatcher分发器,核心方法dispatchEvent,(导演类全局实例化了一个EventDispatcher)
- 谁产生事件? 对接的各平台和cocos框架以及自定义。在目录cocos2d-x-3.3/cocos/下, 执行命令ack dispatchEvent便知
- 事件优先级(后面源码分析将验证)
- 总原则
- A lower priority will be called before the ones that have a higher value.
- 对于SceneGraphPriority(优先级0),高的 Z Order 的节点优先于低的 Z Order 节点,这样确保前面的元素获取触控事件。
- 优先级0: The priority of scene graph will be fixed value 0
- 优先级1: Custom event will use a fixed priority of 1.
- 优先级N: 由
addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority);
指定,但不能指定为0。
- 总原则
//创建监听器
_mouseListener = EventListenerMouse::create();
// 注释:在create的调用中, 会调到基类init方法, 在init方法中
// 会给基类核心成员_onEvent赋值,并将_onEvent和成员
// onMouseMove onMouseUp onMouseDown onMouseScroll
// 之间建立调用关系。
// 相当于提供一层抽象, EventDispatcher只关系EventType,然后根据EventType
// 回调_onEvent, 事件的进一步划分就在_onEvent作处理了。
//监听器的回调赋值
_mouseListener->onMouseMove = [=](Event *event){...};
_mouseListener->onMouseUp = [=](Event *event){...};
_mouseListener->onMouseDown = [=](Event *event){...};
_mouseListener->onMouseScroll = [=](Event *event){...};
//加入监听
_eventDispatcher->addEventListenerWithSceneGraphPriority(_mouseListener, this);
//事件派发
Director::getInstance()->getEventDispatcher()->dispatchEvent(&event);
//回调,在dispatchEvent里最终回调到listener->_onEvent(event);
- 回调过程概括为:
Director::getInstance()->getEventDispatcher()->dispatchEvent(&event);
listener->_onEvent(event);
_onEvent里面在调用更细分的事件函数,eg. onMouseMove
有一处例外就是, EventListenerTouch的_onEvent在init的时候传入的是nullptr, 故在dispatchEvent的时候, 有个专门的函数dispatchTouchEvent用来处理touch事件的回调。
EventListenerCustom* EventDispatcher::addCustomEventListener(const std::string &eventName,
const std::function<void(EventCustom*)>& callback)
{
EventListenerCustom *listener = EventListenerCustom::create(eventName, callback);
//优先级1,自定义事件
addEventListenerWithFixedPriority(listener, 1);
return listener;
}
void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)
{
CCASSERT(listener, "Invalid parameters.");
CCASSERT(!listener->isRegistered(), "The listener has been registered.");
//优先级0被系统暂用,和scene graph相关
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::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);//优先级0
listener->setRegistered(true);
//稍候看这个函数
addEventListener(listener);
}
//上面几个api都调用到这个protected的函数了
void EventDispatcher::addEventListener(EventListener* listener)
{
//_inDispatch的取值含义,它代表有几个事件正在被派发
//0代表EventDispatcher正闲着
//n代表EventDispatcher正在派发n个事件
//(相关代码见dispatchEvent方法里的栈变量DispatchGuard guard(_inDispatch);)
if (_inDispatch == 0)//正闲着,则强制加入
{
forceAddEventListener(listener);
}
else
{//EventDispatcher忙呢,先放在_toAddedListeners
_toAddedListeners.push_back(listener);
}
listener->retain();
}
void EventDispatcher::forceAddEventListener(EventListener* listener)
{
EventListenerVector* listeners = nullptr;
EventListener::ListenerID listenerID = listener->getListenerID();
//_listenerMap是用于存放所用的监听器的
//map的key是监听器id,value是一个容器。
//每一类监听器的id一样,是个字符串,监听器的静态成员
//先找id,也就是先找有这类监听器注册过没
auto itr = _listenerMap.find(listenerID);
if (itr == _listenerMap.end())
{//没注册过,在map里插入一项(不是插入的这个监听器,注意_listenerMap的结构)
listeners = new (std::nothrow) EventListenerVector();
_listenerMap.insert(std::make_pair(listenerID, listeners));
}
else
{//注册过,取得这类型的监听器容器,本质是个vector
listeners = itr->second;
}
//往这个类型的监听器容器里放入这个监听器
listeners->push_back(listener);
if (listener->getFixedPriority() == 0)
{//优先级为0的处理,先setDirty,更新_priorityDirtyFlagMap
setDirty(listenerID, DirtyFlag::SCENE_GRAPH_PRIORITY);
auto node = listener->getAssociatedNode();
CCASSERT(node != nullptr, "Invalid scene graph priority!");
//再将这个listener放入到另外一个成员中(_nodeListenersMap)
//作用我猜测是作为一个辅助数据,空间换时间
associateNodeAndEventListener(node, listener);
if (node->isRunning())
{
//resume
resumeEventListenersForTarget(node);
}
}
else
{
setDirty(listenerID, DirtyFlag::FIXED_PRIORITY);
}
}
- 小结下addEventListener,public的API都调用到addEventListener,将监听器存储到map,针对SceneGraphPriority有额外的辅助数据变量。并设置dirty。
- 留个TODO,setDirty干嘛的?新加入监听器后,设置dirty标志,在派发事件时会先对对应类型监听器按优先级排序,见_priorityDirtyFlagMap和sortEventListeners, dispatchEvent
void EventDispatcher::removeEventListener(EventListener* listener)
{
if (listener == nullptr)
return;
bool isFound = false;
//定义一个回调对象,作用是断开和node的联系,并从容器中清除
auto removeListenerInVector = [&](std::vector<EventListener*>* listeners) {
if (listeners == nullptr)
return;
for (auto iter = listeners->begin(); iter != listeners->end(); ++iter)
{
auto l = *iter;
if (l == listener)
{
CC_SAFE_RETAIN(l);
l->setRegistered(false);
if (l->getAssociatedNode() != nullptr)
{
//断开和node的联系
dissociateNodeAndEventListener(l->getAssociatedNode(), l);
l->setAssociatedNode(nullptr); // nullptr out the node pointer so we don't have any dangling pointers to destroyed nodes.
}
if (_inDispatch == 0)
{
listeners->erase(iter);//从容器中清除
CC_SAFE_RELEASE(l);//不一定和CC_SAFE_RETAIN配对呢? 后面代码还有一次release
}
isFound = true;
break;
}
}
};
//从map的结构中找吧
for (auto iter = _listenerMap.begin(); iter != _listenerMap.end();)
{
auto listeners = iter->second;
auto fixedPriorityListeners = listeners->getFixedPriorityListeners();
auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners();
removeListenerInVector(sceneGraphPriorityListeners);
if (isFound)
{
// fixed #4160: Dirty flag need to be updated after listeners were removed.
setDirty(listener->getListenerID(), DirtyFlag::SCENE_GRAPH_PRIORITY);
}
else
{
removeListenerInVector(fixedPriorityListeners);
if (isFound)
{
setDirty(listener->getListenerID(), DirtyFlag::FIXED_PRIORITY);
}
}
#if CC_NODE_DEBUG_VERIFY_EVENT_LISTENERS
CCASSERT(_inDispatch != 0 ||
!sceneGraphPriorityListeners ||
std::count(sceneGraphPriorityListeners->begin(), sceneGraphPriorityListeners->end(), listener) == 0,
"Listener should be in no lists after this is done if we're not currently in dispatch mode.");
CCASSERT(_inDispatch != 0 ||
!fixedPriorityListeners ||
std::count(fixedPriorityListeners->begin(), fixedPriorityListeners->end(), listener) == 0,
"Listener should be in no lists after this is done if we're not currently in dispatch mode.");
#endif
if (iter->second->empty())
{
_priorityDirtyFlagMap.erase(listener->getListenerID());
auto list = iter->second;
iter = _listenerMap.erase(iter);
CC_SAFE_DELETE(list);
}
else
{
++iter;
}
if (isFound)
break;
}
if (isFound)
{
// 确保前面CC_SAFE_RETAIN
CC_SAFE_RELEASE(listener);
}
else
{
//从待添加的容器中删除
for(auto iter = _toAddedListeners.begin(); iter != _toAddedListeners.end(); ++iter)
{
if (*iter == listener)
{
listener->setRegistered(false);
listener->release();
_toAddedListeners.erase(iter);
break;
}
}
}
}
//其他remove函数也类似,略过
void EventDispatcher::dispatchEvent(Event* event)
{
if (!_isEnabled)
return;
//排序SceneGraph前的准备
updateDirtyFlagForSceneGraph();
//_inDispatch++
DispatchGuard guard(_inDispatch);
//对touch的特殊处理,因为touch的监听器的_onEvent为空
if (event->getType() == Event::Type::TOUCH)
{
dispatchTouchEvent(static_cast<EventTouch*>(event));
return;
}
auto listenerID = __getListenerID(event);
//按优先级排序
sortEventListeners(listenerID);
auto iter = _listenerMap.find(listenerID);
if (iter != _listenerMap.end())
{
//找到了这个类型的监听器容器
auto listeners = iter->second;
//回调
auto onEvent = [&event](EventListener* listener) -> bool {
event->setCurrentTarget(listener->getAssociatedNode());
//真正调到监听器
listener->_onEvent(event);
return event->isStopped();
};
//事件的优先级体现在下面的函数中
dispatchEventToListeners(listeners, onEvent);
}
updateListeners(event);//对该删除的删除,改加入的加入。。。
}
void EventDispatcher::dispatchEventToListeners(EventListenerVector* listeners, const std::function<bool(EventListener*)>& onEvent)
{
bool shouldStopPropagation = false;
auto fixedPriorityListeners = listeners->getFixedPriorityListeners();
auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners();
ssize_t i = 0;
// priority < 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,而不用维护着这样一个index?
//getGt0Index相关的代码就可以省略了
{
auto l = fixedPriorityListeners->at(i);
if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
{
shouldStopPropagation = true;
break;
}
}
}
}
if (sceneGraphPriorityListeners)
{
if (!shouldStopPropagation)
{
// priority == 0, scene graph priority
//优先级为0的顺序决定于visitTarget中的算法
for (auto& l : *sceneGraphPriorityListeners)
{
if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
{
shouldStopPropagation = true;
break;
}
}
}
}
if (fixedPriorityListeners)
{
if (!shouldStopPropagation)
{
// priority > 0
ssize_t size = fixedPriorityListeners->size();
for (; i < size; ++i)
//注意这里的i的作用范围, 同时注意对于fixedPriorityListeners来说
//priority是不会==0的,原因见add linster的代码
{
auto l = fixedPriorityListeners->at(i);
if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
{
shouldStopPropagation = true;
break;
}
}
}
}
}
- visitTarget, 想必和z-order排序相关了
- 事件分发本质是采用的观察者模式: 注册观察者,事件产生并回调,取消注册
- 事件分优先级,由EventDispatcher维护管理,开篇已经提及
- 针对setSwallowsTouches的分析,见cocos2d-x-3.3-009-核心概念和相关类-层
- http://cn.cocos2d-x.org/tutorial/show?id=2154
- https://github.com/chukong/cocos-docs/blob/master/manual/framework/native/v3/event-dispatcher/zh.md
- http://www.cnblogs.com/mmidd/p/3782484.html
- http://www.cnblogs.com/haogj/p/3784896.html
- 观察者模式 https://github.com/cheyiliu/test4java/blob/master/src/test/design/pattern/behavior/testObserver.java
Just build something.