标签:div exception ica 详细 enum 中文 type stop null
源代码版本号来自3.x,转载请注明
cocos2d-x 源代码分析总文件夹
http://blog.csdn.net/u011225840/article/details/31743129
详细的,能够看源代码分析。
enum class Type { TOUCH, KEYBOARD, ACCELERATION, MOUSE, FOCUS, CUSTOM }; Type _type; ///< Event type bool _isStopped; ///< whether the event has been stopped. Node* _currentTarget;
enum class EventCode { BEGAN, MOVED, ENDED, CANCELLED };
void* _userData; ///< User data std::string _eventName;
std::function<void(Event*)> _onEvent; /// Event callback function Type _type; /// Event listener type ListenerID _listenerID; /// Event listener ID bool _isRegistered; /// Whether the listener has been added to dispatcher. int _fixedPriority; // The higher the number, the higher the priority, 0 is for scene graph base priority. Node* _node; // scene graph based priority bool _paused; // Whether the listener is paused bool _isEnabled; // Whether the listener is enabled
/** Enables or disables the listener * @note Only listeners with `enabled` state will be able to receive events. * When an listener was initialized, it's enabled by default. * An event listener can receive events when it is enabled and is not paused. * paused state is always false when it is a fixed priority listener. */ inline void setEnabled(bool enabled) { _isEnabled = enabled; };
/** Sets paused state for the listener * The paused state is only used for scene graph priority listeners. * `EventDispatcher::resumeAllEventListenersForTarget(node)` will set the paused state to `true`, * while `EventDispatcher::pauseAllEventListenersForTarget(node)` will set it to `false`. * @note 1) Fixed priority listeners will never get paused. If a fixed priority doesn't want to receive events, * call `setEnabled(false)` instead. * 2) In `Node`'s onEnter and onExit, the `paused state` of the listeners which associated with that node will be automatically updated. */ inline void setPaused(bool paused) { _paused = paused; };
/// Overrides virtual EventListenerTouchOneByOne* clone() override; virtual bool checkAvailable() override; // public: std::function<bool(Touch*, Event*)> onTouchBegan; std::function<void(Touch*, Event*)> onTouchMoved; std::function<void(Touch*, Event*)> onTouchEnded; std::function<void(Touch*, Event*)> onTouchCancelled;
EventListenerTouchOneByOne* EventListenerTouchOneByOne::clone() { //深拷贝 auto ret = new EventListenerTouchOneByOne(); if (ret && ret->init()) { ret->autorelease(); ret->onTouchBegan = onTouchBegan; ret->onTouchMoved = onTouchMoved; ret->onTouchEnded = onTouchEnded; ret->onTouchCancelled = onTouchCancelled; ret->_claimedTouches = _claimedTouches; ret->_needSwallow = _needSwallow; } else { CC_SAFE_DELETE(ret); } return ret; }
bool EventListenerTouchOneByOne::checkAvailable() { // EventDispatcher will use the return value of 'onTouchBegan' to determine whether to pass following 'move', 'end' // message to 'EventListenerTouchOneByOne' or not. So 'onTouchBegan' needs to be set. //OneByOne仅仅须要onTouchBegan不为空,则能够觉得其是可用的。什么是可用性,当在dispatcher进行事件分发时,假设一个Listener是不可用的。则不会将该事件分发给他。if (onTouchBegan == nullptr) { CCASSERT(false, "Invalid EventListenerTouchOneByOne!"); return false; } return true; }
std::vector<Touch*> _claimedTouches; bool _needSwallow;
std::function<void(const std::vector<Touch*>&, Event*)> onTouchesBegan; std::function<void(const std::vector<Touch*>&, Event*)> onTouchesMoved; std::function<void(const std::vector<Touch*>&, Event*)> onTouchesEnded; std::function<void(const std::vector<Touch*>&, Event*)> onTouchesCancelled;
bool EventListenerTouchAllAtOnce::checkAvailable() { if (onTouchesBegan == nullptr && onTouchesMoved == nullptr && onTouchesEnded == nullptr && onTouchesCancelled == nullptr) { CCASSERT(false, "Invalid EventListenerTouchAllAtOnce!"); return false; } return true; }
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; };
size_t EventDispatcher::EventListenerVector::size() const { //vector内部的size大小是两个list的和 size_t ret = 0; if (_sceneGraphListeners) ret += _sceneGraphListeners->size(); if (_fixedListeners) ret += _fixedListeners->size(); return ret; }
void EventDispatcher::EventListenerVector::push_back(EventListener* listener) { //查看listener的priority。假设为0,增加sceneGraphList,否则增加fixedList if (listener->getFixedPriority() == 0) { if (_sceneGraphListeners == nullptr) { _sceneGraphListeners = new std::vector<EventListener*>(); _sceneGraphListeners->reserve(100); } _sceneGraphListeners->push_back(listener); } else { if (_fixedListeners == nullptr) { _fixedListeners = new std::vector<EventListener*>(); _fixedListeners->reserve(100); } _fixedListeners->push_back(listener); } }
void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node) { CCASSERT(listener && node, "Invalid parameters."); CCASSERT(!listener->isRegistered(), "The listener has been registered."); //检查Listener可用性 if (!listener->checkAvailable()) return; //设置listener相关属性 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."); //Fixed类型的事件优先级不能是0 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); }
EventListenerCustom* EventDispatcher::addCustomEventListener(const std::string &eventName, const std::function<void(EventCustom*)>& callback) { //custom类的事件加入是通过eventName 和 eventcallBack来进行加入的 EventListenerCustom *listener = EventListenerCustom::create(eventName, callback); //custom的事件优先级被默觉得1 addEventListenerWithFixedPriority(listener, 1); return listener; }
。
。。
。)
void EventDispatcher::addEventListener(EventListener* listener) { //假设当前Dispatcher正在进行事件Dispatch,则放到toAddList中。 if (_inDispatch == 0) { forceAddEventListener(listener); } else { // std::vector _toAddedListeners.push_back(listener); } listener->retain(); }
void EventDispatcher::forceAddEventListener(EventListener* listener) { EventListenerVector* listeners = nullptr; EventListener::ListenerID listenerID = listener->getListenerID(); //找到该类eventlistener的vector。此处的vector是EventVector auto itr = _listenerMap.find(listenerID); //假设没有找到,则须要向map中加入一个pair if (itr == _listenerMap.end()) { listeners = new EventListenerVector(); _listenerMap.insert(std::make_pair(listenerID, listeners)); } else { listeners = itr->second; } //将该类别listenerpush_back进去(这个函数调用的是EventVector的pushback哦) listeners->push_back(listener); //假设优先级是0。则设置为graph。 if (listener->getFixedPriority() == 0) { //设置该listenerID的DirtyFlag //(setDirty函数能够这样理解,每一个ListenerID都有特定的dirtyFlag。每次进行add操作后,都要更新该ID的flag) setDirty(listenerID, DirtyFlag::SCENE_GRAPH_PRIORITY); //假设是sceneGraph类的事件。则须要处理两个方面: //1.将node 与event 关联 //2.假设该node是执行中的。则须要恢复其事件(由于默认的sceneGraph listener的状态时pause) //添加该listener与node的关联 auto node = listener->getAssociatedNode(); CCASSERT(node != nullptr, "Invalid scene graph priority!"); associateNodeAndEventListener(node, listener); //恢复node的执行状态 if (node->isRunning()) { resumeEventListenersForTarget(node); } } else { setDirty(listenerID, DirtyFlag::FIXED_PRIORITY); } }
void EventDispatcher::associateNodeAndEventListener(Node* node, EventListener* listener) { //将listener与node关联,先从map中找到与该node相关的listener vector std::vector<EventListener*>* listeners = nullptr; auto found = _nodeListenersMap.find(node); if (found != _nodeListenersMap.end()) { listeners = found->second; } else { listeners = new std::vector<EventListener*>(); _nodeListenersMap.insert(std::make_pair(node, listeners)); } //vector内加入该listener,这里的vector 是std::vector listeners->push_back(listener); }
void EventDispatcher::resumeEventListenersForTarget(Node* target, bool recursive/* = false */) { //恢复Node的执行状态 auto listenerIter = _nodeListenersMap.find(target); if (listenerIter != _nodeListenersMap.end()) { auto listeners = listenerIter->second; for (auto& l : *listeners) { l->setPaused(false); } } // toAdd List中也要进行恢复 for (auto& listener : _toAddedListeners) { if (listener->getAssociatedNode() == target) { listener->setPaused(false); } } //将该Node 与 node的child 都放到dirtyNode中,来记录与event相关的node setDirtyForNode(target); if (recursive) { const auto& children = target->getChildren(); for (const auto& child : children) { resumeEventListenersForTarget(child, true); } } }
void EventDispatcher::removeEventListener(EventListener* listener) { //说在前面,移除一个事件的代价比較大,假设没有必要,请不要无故移除事件。 //删除一个listener的步骤: if (listener == nullptr) return; bool isFound = false; //lambda函数。函数从std::vector<EventListener*>* listeners 中移除该listener 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) { //找到后的处理方法,标记状态位,并处理关联Node CC_SAFE_RETAIN(l); l->setRegistered(false); if (l->getAssociatedNode() != nullptr) { dissociateNodeAndEventListener(l->getAssociatedNode(), l); l->setAssociatedNode(nullptr); // NULL out the node pointer so we don't have any dangling pointers to destroyed nodes. } //当前没有在分发事件 则直接从listeners中移除该listener(由于标记了状态未。假设此时在分发事件。则会等结束后再移除) if (_inDispatch == 0) { listeners->erase(iter); CC_SAFE_RELEASE(l); } isFound = true; break; } } }; for (auto iter = _listenerMap.begin(); iter != _listenerMap.end();) { //从listenersmap 中遍历全部。拿出全部的vector auto listeners = iter->second; auto fixedPriorityListeners = listeners->getFixedPriorityListeners(); auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners(); //从graphList中寻找。找到后须要更新该listenerID的dirty flag。 removeListenerInVector(sceneGraphPriorityListeners); if (isFound) { // fixed #4160: Dirty flag need to be updated after listeners were removed. setDirty(listener->getListenerID(), DirtyFlag::SCENE_GRAPH_PRIORITY); } //从fixedList中寻找 else { removeListenerInVector(fixedPriorityListeners); if (isFound) { setDirty(listener->getListenerID(), DirtyFlag::FIXED_PRIORITY); } } //假设vector在删除后是空的,则须要移除该vector。而且将对应的listenerID从_priorityDirtyFlagMap中移除。 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_RELEASE(listener); } //假设在上述过程中未找到。则从toAddList中寻找 else { for(auto iter = _toAddedListeners.begin(); iter != _toAddedListeners.end(); ++iter) { if (*iter == listener) { listener->setRegistered(false); listener->release(); _toAddedListeners.erase(iter); break; } } } }
void EventDispatcher::removeEventListenersForListenerID(const EventListener::ListenerID& listenerID) { auto listenerItemIter = _listenerMap.find(listenerID); if (listenerItemIter != _listenerMap.end()) { auto listeners = listenerItemIter->second; auto fixedPriorityListeners = listeners->getFixedPriorityListeners(); auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners(); //啊哦 又是一个lambda函数。将std::vector<EventListener*>* listenerVector中的Listener所有移除 auto removeAllListenersInVector = [&](std::vector<EventListener*>* listenerVector){ if (listenerVector == nullptr) return; for (auto iter = listenerVector->begin(); iter != listenerVector->end();) { //设置要删除的listener状态,清空与其相关的node信息 auto l = *iter; l->setRegistered(false); if (l->getAssociatedNode() != nullptr) { dissociateNodeAndEventListener(l->getAssociatedNode(), l); l->setAssociatedNode(nullptr); // NULL out the node pointer so we don't have any dangling pointers to destroyed nodes. } if (_inDispatch == 0) { iter = listenerVector->erase(iter); CC_SAFE_RELEASE(l); } else { ++iter; } } }; //两种类型的事件哦 removeAllListenersInVector(sceneGraphPriorityListeners); removeAllListenersInVector(fixedPriorityListeners); // Remove the dirty flag according the 'listenerID'. // No need to check whether the dispatcher is dispatching event. _priorityDirtyFlagMap.erase(listenerID); if (!_inDispatch) { listeners->clear(); delete listeners; _listenerMap.erase(listenerItemIter); } } //toAddList 的清理,真可怜,还没来得及进入家门就要被扫地出门了么。。。。 for (auto iter = _toAddedListeners.begin(); iter != _toAddedListeners.end();) { if ((*iter)->getListenerID() == listenerID) { (*iter)->setRegistered(false); (*iter)->release(); iter = _toAddedListeners.erase(iter); } else { ++iter; } } }
void EventDispatcher::removeEventListenersForType(EventListener::Type listenerType) { if (listenerType == EventListener::Type::TOUCH_ONE_BY_ONE) { removeEventListenersForListenerID(EventListenerTouchOneByOne::LISTENER_ID); } else if (listenerType == EventListener::Type::TOUCH_ALL_AT_ONCE) { removeEventListenersForListenerID(EventListenerTouchAllAtOnce::LISTENER_ID); } else if (listenerType == EventListener::Type::MOUSE) { removeEventListenersForListenerID(EventListenerMouse::LISTENER_ID); } else if (listenerType == EventListener::Type::ACCELERATION) { removeEventListenersForListenerID(EventListenerAcceleration::LISTENER_ID); } else if (listenerType == EventListener::Type::KEYBOARD) { removeEventListenersForListenerID(EventListenerKeyboard::LISTENER_ID); } else { CCASSERT(false, "Invalid listener type!"); } }
void EventDispatcher::removeCustomEventListeners(const std::string& customEventName) { removeEventListenersForListenerID(customEventName); }
void EventDispatcher::removeAllEventListeners() { bool cleanMap = true; std::vector<EventListener::ListenerID> types(_listenerMap.size()); for (const auto& e : _listenerMap) { if (_internalCustomListenerIDs.find(e.first) != _internalCustomListenerIDs.end()) { cleanMap = false; } else { types.push_back(e.first); } } for (const auto& type : types) { removeEventListenersForListenerID(type); } if (!_inDispatch && cleanMap) { _listenerMap.clear(); } }
ok,让我们静静赞赏源代码吧。
void EventDispatcher::dispatchEvent(Event* event) { if (!_isEnabled) return; //为dirtyNodesVector中的dirtyNode更新Scene Flag。updateDirtyFlagForSceneGraph(); DispatchGuard guard(_inDispatch); //特殊touch事件,转到特殊的touch事件处理 if (event->getType() == Event::Type::TOUCH) { dispatchTouchEvent(static_cast<EventTouch*>(event)); return; } //依据事件的类型。获取事件的ID auto listenerID = __getListenerID(event); //依据事件ID,将该类事件进行排序(先响应谁) sortEventListeners(listenerID); auto iter = _listenerMap.find(listenerID); if (iter != _listenerMap.end()) { auto listeners = iter->second; //该类事件的lambda函数 auto onEvent = [&event](EventListener* listener) -> bool{ //设置event的target event->setCurrentTarget(listener->getAssociatedNode()); //调用响应函数 listener->_onEvent(event); //返回是否已经停止 return event->isStopped(); }; //将该类事件的listeners 和 该类事件的 lambda函数传给该函数 dispatchEventToListeners(listeners, onEvent); } //更新该事件相关的listener updateListeners(event); }
void EventDispatcher::dispatchEventToListeners(EventListenerVector* listeners, const std::function<bool(EventListener*)>& onEvent) { bool shouldStopPropagation = false; auto fixedPriorityListeners = listeners->getFixedPriorityListeners(); auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners(); //总体操作流程分为三个部分,处理优先级<0,=0,>0三个部分 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) { auto l = fixedPriorityListeners->at(i); // onEvent(l)的操作调用了event的callBack,而且会返回是否停止,假设停止后,则将shouldStopPropagation标记为true //在其后面的listeners则不会响应到该事件(这里能够看出触摸事件是怎样被吞噬的) if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l)) { shouldStopPropagation = true; break; } } } } if (sceneGraphPriorityListeners) { if (!shouldStopPropagation) { // priority == 0, scene graph priority 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) { auto l = fixedPriorityListeners->at(i); if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l)) { shouldStopPropagation = true; break; } } } } }
void EventDispatcher::dispatchTouchEvent(EventTouch* event) { //先将EventListeners排序 sortEventListeners(EventListenerTouchOneByOne::LISTENER_ID); sortEventListeners(EventListenerTouchAllAtOnce::LISTENER_ID); auto oneByOneListeners = getListeners(EventListenerTouchOneByOne::LISTENER_ID); auto allAtOnceListeners = getListeners(EventListenerTouchAllAtOnce::LISTENER_ID); // If there aren't any touch listeners, return directly. if (nullptr == oneByOneListeners && nullptr == allAtOnceListeners) return; //mutableTouches是用来处理allAtOnce的 bool isNeedsMutableSet = (oneByOneListeners && allAtOnceListeners); //这些touch都来自该事件 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(); //遍历touches,每个touch都来自于同一个事件 for (; touchesIter != originalTouches.end(); ++touchesIter) { bool isSwallowed = false; //事件处理的lambda函数 auto onTouchEvent = [&](EventListener* l) -> bool { // Return true to break EventListenerTouchOneByOne* listener = static_cast<EventListenerTouchOneByOne*>(l); // Skip if the listener was removed. if (!listener->_isRegistered) return false; event->setCurrentTarget(listener->_node); //claimed代表该listener是否接收了该touch(Began返回true or false) bool isClaimed = false; std::vector<Touch*>::iterator removedIter; //依据eventNode的不同,会调用不同的callBack函数 EventTouch::EventCode eventCode = event->getEventCode(); if (eventCode == EventTouch::EventCode::BEGAN) { //调用began if (listener->onTouchBegan) { isClaimed = listener->onTouchBegan(*touchesIter, event); if (isClaimed && listener->_isRegistered) { //返回true后 将该touch放入该listener的claimedTouches listener->_claimedTouches.push_back(*touchesIter); } } } //假设是后三个move end cancel else if (listener->_claimedTouches.size() > 0 && ((removedIter = std::find(listener->_claimedTouches.begin(), listener->_claimedTouches.end(), *touchesIter)) != listener->_claimedTouches.end())) { isClaimed = true; //调用对应的callBack 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(), ""); //假设接收该touch而且须要吞噬该touch。会有两个影响 //1.Touches(standard 触摸机制)的触摸操作都接收不到该touch了 //2.由于返回值是true,在调用dispatchEventToListeners时。在该node之后的node将会不再接收该touch if (isClaimed && listener->_isRegistered && listener->_needSwallow) { if (isNeedsMutableSet) { mutableTouchesIter = mutableTouches.erase(mutableTouchesIter); isSwallowed = true; } return true; } return false; }; //结合上面的dispatchEventToListeners的源代码分析。能够看出新版本号的OneByOne touch机制是这种: //1.listener依据Node的优先级排序后。依次响应。值得注意的是,新版本号的优先级是依据Node的global Zorder来的。而不是2.x的触摸优先级。
//2.当TouchEvent Began来了之后,全部的listener会依次影响Touch Began。
然后再依次响应Touch Move...而不是一个listener响应完 //began move end之后 轮到下一个listener响应的顺序。 //3.吞噬操作仅仅有发生在began return true后才干够发生 dispatchEventToListeners(oneByOneListeners, onTouchEvent); if (event->isStopped()) { return; } if (!isSwallowed) ++mutableTouchesIter; } } // // process standard handlers 2nd // //相比于OneByOne。AllAtOnce要简单很多。值得注意的是被吞噬的touch也不会被AllAtOnce响应到 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; }; dispatchEventToListeners(allAtOnceListeners, onTouchesEvent); if (event->isStopped()) { return; } } updateListeners(event); }
void EventDispatcher::dispatchCustomEvent(const std::string &eventName, void *optionalUserData) { EventCustom ev(eventName); ev.setUserData(optionalUserData); dispatchEvent(&ev); }
cocos2d-x 源代码分析 : EventDispatcher、EventListener、Event 源代码分析 (新触摸机制,新的NotificationCenter机制)
标签:div exception ica 详细 enum 中文 type stop null
原文地址:http://www.cnblogs.com/gavanwanggw/p/7323383.html