一、使用示例

EventCustom:自定义事件

  1. std::string eventName("自定义事件"); // event name
  2. EventCustom event(eventName); // event
  3. void *pData = ......; // event data
  4. EventListenerCustom *pListener = nullptr; // event listener
  5. auto listenerPriority = 1; // listener priority
  6. std::function<void(EventCustom*)> callback = nullptr; // event callback
  7. auto dispatcher = Director::getInstance()->getEventDispatcher(); // event dispatcher
  8. callback = [](EventCustom* event){
  9. char* buf = static_cast<char*>(event->getUserData());
  10. }
  11. pListener = EventListenerCustom::create(eventName, callback);
  12. event.setUserData(pData);
  13. // register listener
  14. dispatcher->addEventListenerWithFixedPriority(pListener, listenerPriority);
  15. // dispatch event
  16. _eventDispatcher->dispatchEvent(&pEvent);
  17. // unregister listener
  18. dispatcher->removeEventListener(pListener);

TouchOneByOne:单点触摸事件

  1. // Adds Touch Event Listener
  2. auto listener = EventListenerTouchOneByOne::create();
  3. listener->setSwallowTouches(true);
  4. listener->onTouchBegan = CC_CALLBACK_2(Box2DView::onTouchBegan, this);
  5. listener->onTouchMoved = CC_CALLBACK_2(Box2DView::onTouchMoved, this);
  6. listener->onTouchEnded = CC_CALLBACK_2(Box2DView::onTouchEnded, this);
  7. _eventDispatcher->addEventListenerWithFixedPriority(listener, -10);
  8. bool Box2DView::onTouchBegan(Touch* touch, Event* event)
  9. {
  10. // 世界坐标转换为本地坐标
  11. auto localPos = convertToNodeSpace( touch->getLocation() );
  12. return true; // 接收后续的touch,并且吞噬touch,后续listener不会再接收到。
  13. }
  14. void Box2DView::onTouchMoved(Touch* touch, Event* event) { }
  15. void Box2DView::onTouchEnded(Touch* touch, Event* event) { }

TouchAllAtOnce:多点触摸事件

  1. auto listener = EventListenerTouchAllAtOnce::create();
  2. listener->onTouchesBegan = CC_CALLBACK_2(ForceTouchTest::onTouchesBegan, this);
  3. listener->onTouchesMoved = CC_CALLBACK_2(ForceTouchTest::onTouchesMoved, this);
  4. listener->onTouchesEnded = CC_CALLBACK_2(ForceTouchTest::onTouchesEnded, this);
  5. _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
  6. void ForceTouchTest::onTouchesBegan(const std::vector<cocos2d::Touch*>& touches,
  7. cocos2d::Event* event) { }
  8. void ForceTouchTest::onTouchesMoved(const std::vector<cocos2d::Touch*>& touches,
  9. cocos2d::Event* event) {
  10. for(auto& t : touches) { }
  11. }
  12. void ForceTouchTest::onTouchesEnded(const std::vector<cocos2d::Touch*>& touches,
  13. cocos2d::Event* event) { }

EventKeyBoard:键盘事件

  1. auto keyboardListener = EventListenerKeyboard::create();
  2. keyboardListener->onKeyPressed = CC_CALLBACK_2(Box2DView::onKeyPressed, this);
  3. keyboardListener->onKeyReleased = CC_CALLBACK_2(Box2DView::onKeyReleased, this);
  4. _eventDispatcher->addEventListenerWithFixedPriority(keyboardListener, -11);
  5. void Box2DView::onKeyPressed(EventKeyboard::KeyCode code, Event* event)
  6. {
  7. static_cast<unsigned char>(code)
  8. }
  9. void Box2DView::onKeyReleased(EventKeyboard::KeyCode code, Event* event)
  10. {
  11. static_cast<unsigned char>(code)
  12. }

以上例子展示了一个事件系统的基本组成元素和其功能。

  • Event:事件
    • name:事件名称
    • data:事件的数据
  • EventListener:事件监听者
    • callback:监听回调
    • event name:监听的事件名称
  • EventDispatcher:事件分发者

    • register listener:注册监听
      • priority:监听优先级
    • dispatch event:派发事件
    • unregister listener:注销监听

      二、事件

      cocos中Event类是所有事件的父类,目前引擎所使用到的Event类型如下:
  • EventTouch:触摸事件

  • EventKeyboard:键盘事件
  • EventMouse:鼠标事件
  • EventCustom:自定义事件
    • 在引擎内部自定义了很多事件,如director中在渲染的各个环节分发了自定义事件,如schedule update前/后,UI树遍历前/后,UI树绘制前/后。
  • EventAcceleration:重力加速度事件
  • EventFocus:焦点事件
  • EventController:未知,待完成

    Event

    ```cpp

class CC_DLL Event : public Ref { Event(Type type); Type getType(); // 事件类型,枚举

  1. void stopPropagation(); // 本事件停止“传播”,EventDispatcher不会再分发此事件给后续的监听者。
  2. bool isStopped(); // 是否已停止“传播”
  3. // 事件关联的target
  4. // 当且仅当addEventListenerWithSceneGraphPriority时传入的node,否则为nullptr
  5. // 当然你可以通过继承Event通过setCurrentTarget传入target
  6. Node* getCurrentTarget() { return _currentTarget; }

};

  1. 我们再学习一下,最常用的两个事件,EventTouchEventCustom
  2. <a name="GbNze"></a>
  3. ## EventTouch
  4. 触摸事件。
  5. ```cpp
  6. class CC_DLL EventTouch : public Event {
  7. public:
  8. enum class EventCode {
  9. BEGAN, // 手指按下屏幕,触摸开始
  10. MOVED, // 手指滑动屏幕
  11. ENDED, // 手指离开屏幕
  12. CANCELLED // 触摸被动取消,比如滑动屏幕时,来电、信息等紧急事件
  13. };
  14. EventCode getEventCode(); // 触摸点的状态
  15. // 每个touch记录了当前屏幕上处于相同状态的一组触摸点的集合。
  16. // 比如一组滑动的触摸事件,代表一次滑动
  17. const std::vector<Touch*>& getTouches() const { return _touches; }
  18. };

Touch

触摸信息。

  1. class CC_DLL Touch : public Ref {
  2. public:
  3. enum class DispatchMode { // how the touches are dispatched.
  4. ALL_AT_ONCE, /** All at once. */
  5. ONE_BY_ONE, /** One by one. */
  6. };
  7. Vec2 getLocation() const; // 返回当前触摸的世界坐标
  8. Vec2 getLocationInView() const; // 返回当前触摸的屏幕坐标
  9. Vec2 getPreviousLocation() const; // 返回上一个触摸的世界坐标
  10. Vec2 getPreviousLocationInView() const; // 返回上一个触摸的屏幕坐标
  11. Vec2 getStartLocation() const; // 返回触摸起始点的世界坐标
  12. Vec2 getStartLocationInView() const; // 返回触摸起始点的屏幕坐标
  13. //Returns the delta of 2 current touches locations in screen coordinates
  14. Vec2 getDelta() const;
  15. //Get touch id,touch唯一标识
  16. int getID();
  17. };

EventCustom

自定义事件,像EventTouch、EventMouse这些都是从硬件产生的事件,我们也可以通过EventCustom来产生自定义的事件。
部分源码如下:

  1. class CC_DLL EventCustom : public Event
  2. {
  3. EventCustom(const std::string& eventName); // 事件名称,类似ID,唯一区分EventCustom
  4. void setUserData(void* data); // 传递参数给listener
  5. void* getUserData();
  6. const std::string& getEventName();
  7. };

下面是引擎内部自定义的一些事件。

  1. // director->setNextScene前后。
  2. const char *Director::EVENT_BEFORE_SET_NEXT_SCENE = "director_before_set_next_scene";
  3. const char *Director::EVENT_AFTER_SET_NEXT_SCENE = "director_after_set_next_scene";
  4. // director->setProjection,投影矩阵发生变化
  5. const char *Director::EVENT_PROJECTION_CHANGED = "director_projection_changed";
  6. // draw指的是:UI树遍历 + 绘制渲染。
  7. const char *Director::EVENT_BEFORE_DRAW = "director_before_draw";
  8. const char *Director::EVENT_AFTER_DRAW = "director_after_draw";
  9. // UI树遍历后
  10. const char *Director::EVENT_AFTER_VISIT = "director_after_visit";
  11. // shcedule update前后
  12. const char *Director::EVENT_BEFORE_UPDATE = "director_before_update";
  13. const char *Director::EVENT_AFTER_UPDATE = "director_after_update";
  14. // director reset
  15. const char *Director::EVENT_RESET = "director_reset";
  16. // The application will come to foreground.
  17. // This message is posted in cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxRenderer.cpp.
  18. #define EVENT_COME_TO_FOREGROUND "event_come_to_foreground"
  19. // The renderer[android:GLSurfaceView.Renderer WP8:Cocos2dRenderer] was recreated.
  20. // This message is used for reloading resources before renderer is recreated on Android/WP8.
  21. // This message is posted in cocos/platform/android/javaactivity.cpp and cocos\platform\wp8-xaml\cpp\Cocos2dRenderer.cpp.
  22. #define EVENT_RENDERER_RECREATED "event_renderer_recreated"
  23. // The application will come to background.
  24. // This message is used for doing something before coming to background, such as save RenderTexture.
  25. // This message is posted in cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxRenderer.cpp and cocos\platform\wp8-xaml\cpp\Cocos2dRenderer.cpp.
  26. #define EVENT_COME_TO_BACKGROUND "event_come_to_background"

三、事件监听

基类EventListener是所有Listener的父类,目前已有如下几种Listener,基本上和event对应:

  • EventListenerTouchOneByOne:单点触摸,一次处理一个触摸点信息。
  • EventListenerTouchAllAtOnce:多点触摸,一次处理当前所有触摸点信息。
  • EventListenerMouse:鼠标事件监听
  • EventListenerKeyboard:键盘事件监听
  • EventListenerFocus:焦点事件监听
  • EventListenerCustom:自定义事件监听
    • EventListenerAssetsManagerEx:下载结束监听
  • EventListenerAcceleration:重力加速度事件监听
  • EventListenerController:未知,待完成

    EventLisenter

    ```cpp

class CC_DLL EventListener : public Ref { public: typedef std::string ListenerID; // 见下面解释 virtual bool checkAvailable() = 0; // 决定了diaptcher.addEventListener能否成功 virtual EventListener* clone() = 0; // 复制

  1. void setEnabled(bool enabled); // true才可能接收事件,默认true
  2. bool isEnabled();

protected: // 只用于scene graph priority listeners,针对Node会有onEnter、onExit情况 // fixed priority listeners的paused总为false void setPaused(bool paused); bool isPaused();

  1. // 是否注册到eventdispatcher中。
  2. void setRegistered(bool registered);
  3. bool isRegistered();
  4. //只针对fixed priority listeners,不能设置为0,因为scene graph priority是0
  5. void setFixedPriority(int fixedPriority) { _fixedPriority = fixedPriority; }
  6. int getFixedPriority() const { return _fixedPriority; }
  7. /** Sets the node associated with this listener */
  8. //scene graph priority listeners注册时,绑定的Node
  9. void setAssociatedNode(Node* node) { _node = node; }
  10. Node* getAssociatedNode() const { return _node; }

};

  1. 到这里我们发现有好几个typeID好相近,EventTypeEventListenerTypeEventListenerID,我们来总结一下它们的关系:
  2. | Event::Type<br />(枚举) | EventListener::Type<br />(枚举) | EventListenerID<br />(string |
  3. | :---: | :---: | :---: |
  4. | TOUCH | TOUCH_ONE_BY_ONE | __cc_touch_one_by_one |
  5. | | TOUCH_ALL_AT_ONCE | __cc_touch_all_at_once |
  6. | KEYBOARD | KEYBOARD | __cc_keyboard |
  7. | ACCELERATION | ACCELERATION | __cc_acceleration |
  8. | MOUSE | MOUSE | __cc_mouse |
  9. | FOCUS | FOCUS | __cc_focus_event |
  10. | GAME_CONTROLLER | GAME_CONTROLLER | __cc_controller |
  11. | CUSTOM | CUSTOM | getEventName() |
  12. **Touch为什么不是一一对应?**<br />一个touch事件会携带当前屏幕上的所有触摸点的触摸信息,保存在一个vector中,TOUCH_ONE_BY_ONElistenter对于vector中每个touch都会回调一次,即一次处理一个触摸信息,TOUCH_ALL_AT_ONCElistener只回调一次,传入的参数就是vector,一次处理当前触摸事件中的所有触摸点的触摸信息。
  13. **为什么还要一个ListenerID?**<br />既然都有一个EventListener Type了,还有ListenerID干嘛?<br />一个EventType可能对应多个EventListenerType,而一个EventListenerType可能也对应多个EventListenerID,比如EventListnerCustom,每个eventname就是一个listenerID。可以这么理解,EventListenerType是划分大类,EventlistenerID是划分细类,ListenerID才是真正决定了该Listener接收具体哪个Event
  14. <a name="U7LhT"></a>
  15. ## EventListenerTouchOneByOne
  16. 单点触摸。
  17. ```cpp
  18. class CC_DLL EventListenerTouchOneByOne : public EventListener
  19. {
  20. public:
  21. static const std::string LISTENER_ID; // __cc_touch_one_by_one
  22. // LISTENER_ID是固定的__cc_touch_one_by_one,所以create时不需要这个参数了。
  23. static EventListenerTouchOneByOne* create();
  24. // 是否阻止触摸touch事件向后续listener分发,ontouchbegan必须返回true才能生效
  25. // 也就是说自己必须处理这些touch事件。
  26. void setSwallowTouches(bool needSwallow);
  27. bool isSwallowTouches();
  28. public:
  29. // 注意到touchBegan返回了一个bool值,告诉eventdispatcher是否应该将该触摸点的
  30. // 后续触摸数据传递给这个listener
  31. // 单点触摸最好加上状态判断,防止连续点击,多点点击导致touch回调被执行多次产生的潜在bug
  32. // 绑定Node节点的可见性不会影响EventDispatcher对该listener的事件分发,所以
  33. // 我们也要考虑可见性的逻辑判断
  34. typedef std::function<bool(Touch*, Event*)> ccTouchBeganCallback;
  35. typedef std::function<void(Touch*, Event*)> ccTouchCallback;
  36. ccTouchBeganCallback onTouchBegan;
  37. ccTouchCallback onTouchMoved;
  38. ccTouchCallback onTouchEnded;
  39. ccTouchCallback onTouchCancelled;
  40. // onTouchBegan例子
  41. bool Node::onTouchBegan(Touch* touch, Event* event){
  42. // isHandling: 是否正在处理触摸,防止多个触摸点或者连续点击,连续重复触发touch回调。
  43. // isVisble: eventListener绑定节点的可见性,不可见应该不需要触发touch回调。
  44. if(!isHandling || !isVisble){
  45. return false;
  46. }
  47. //绑定节点的父节点不可见,也不应该触发touch回调。
  48. for(auto parent = getParent(); parent != nullptr; parent = parent->getParent()){
  49. if(!parent->isVisble()){
  50. return false;
  51. }
  52. }
  53. isHandling = true;
  54. }
  55. };

EventListenerTouchOneByOne的swallowTouches和onTouchBegan的影响

TouchID onTouchBegan swallowTouches OneByOne AllAtOnce
1 true true 调用 无调用
2 true false 调用 调用
3 false true 无调用 调用
4 false false 无调用 调用

EventListenerTouchAllAtOnce

多点触摸。

  1. class CC_DLL EventListenerTouchAllAtOnce : public EventListener {
  2. public:
  3. static const std::string LISTENER_ID; // __cc_touch_all_at_once
  4. // LISTENER_ID是固定的,所以create时不需要这个参数了。
  5. static EventListenerTouchAllAtOnce* create();
  6. public:
  7. // 可以看到,相比于EventListenerTouchOneByOne,这个传入是touch的vector
  8. // 表示当前屏幕上接收到的所有触摸数据。
  9. typedef std::function<void(const std::vector<Touch*>&, Event*)> ccTouchesCallback;
  10. // 触摸回调是public类型的,直接赋值设置。
  11. ccTouchesCallback onTouchesBegan;
  12. ccTouchesCallback onTouchesMoved;
  13. ccTouchesCallback onTouchesEnded;
  14. ccTouchesCallback onTouchesCancelled;
  15. };

每次的触摸事件,EventDispatcher会将每个触摸点单独作用在每个EventListenerTouchOneByOne,然后再将所有的触摸点集合作用在EventListenerTouchAllAtOnce上,如果允许的话。

EventListenerCustom

  1. class CC_DLL EventListenerCustom : public EventListener {
  2. public:
  3. static EventListenerCustom* create(const std::string& eventName,
  4. const std::function<void(EventCustom*)>& callback);
  5. // 决定了diaptcher.addEventListener能否成功
  6. virtual bool checkAvailable() override;
  7. virtual EventListenerCustom* clone() override; // 复制
  8. protected:
  9. std::function<void(EventCustom*)> _onCustomEvent;
  10. friend class LuaEventListenerCustom;
  11. };

四、事件分发

API

注册监听

  1. class CC_DLL EventDispatcher : public Ref {
  2. public:
  3. // 注册SceneGraphListener
  4. // 按node在UI树中的遍历顺序来决定优先级,越先绘制,Listener优先级越低。
  5. void addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node);
  6. // 注册FixedPriorityListener,即指定优先级的listener
  7. // fixedPriority: listener的优先级,固定数值优先级的,数值越小,优先级越高
  8. // 不能为0,SceneGraphListener默认fixedPriority = 0
  9. void addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority);
  10. // 注册自定义的事件监听,本质上是:
  11. // addEventListenerWithFixedPriority(listener, 1)
  12. EventListenerCustom* addCustomEventListener(
  13. const std::string& eventName,
  14. const std::function<void(EventCustom*)>& callback );
  15. }

注销监听

  1. class CC_DLL EventDispatcher : public Ref {
  2. // 删除具体的listener
  3. void removeEventListener(EventListener* listener);
  4. // 删除指定Type的listener
  5. void removeEventListenersForType(EventListener::Type listenerType);
  6. // 删除所有关联node的listener,还可以递归删除node子节点关联的listener。
  7. void removeEventListenersForTarget(Node* target, bool recursive = false);
  8. // 按listenerID删除自定义listener
  9. void removeCustomEventListeners(const std::string& customEventName);
  10. // 删除所有listener
  11. void removeAllEventListeners();
  12. }

暂停/恢复监听

  1. class EventListener : public Ref{
  2. public:
  3. // setEnabled(false),listener将不会接收到任何事件
  4. // setEnabled(true),恢复接收事件
  5. // 默认为true
  6. void setEnabled(bool enabled);
  7. protected:
  8. // 只用于scene graph priority listeners,针对Node会有onEnter、onExit情况
  9. // fixed priority listeners的paused总为false
  10. void setPaused(bool paused);
  11. // 是否注册到Dispatcher中,由dispatcher调用
  12. void setRegistered(bool registered);
  13. }
  14. class EventDispatcher : public Ref{
  15. // 用于通过addEventListenerWithSceneGraphPriority注册的Listener
  16. // 这些listener
  17. // 在node onExit()的时候暂停接收事件
  18. // 在node onEnter()的时候恢复接收事件
  19. // 实际调用的是上面EventListener的setPause()
  20. void resumeEventListenersForTarget(Node* target, bool recursive = false)
  21. void pauseEventListenersForTarget(Node* target, bool recursive = false)
  22. }
  23. // 感觉EventListener有很多个状态开关,我们捋一下:
  24. // 在EventDispatcher中,要使一个EventListener能回调的条件如下;
  25. if (l->isEnabled() && // 首先必须Enable,这个可由外部调用决定
  26. !l->isPaused() && // 必须not pause,这个是针对scene graph priority listeners
  27. // 上面的pause、resume可以决定
  28. // fixed priority 默认为true
  29. l->isRegistered() && // 这个由于eventdispatcher内部调用setRegistered决定
  30. onEvent(l) // lisenter callback
  31. ){
  32. ......
  33. }

分发事件

  1. class EventDispatcher : public Ref{
  2. public:
  3. // 派发事件,会触发updateListeners,处理toRemoved、toAdded。
  4. void dispatchEvent(Event* event);
  5. // 派发自定义事件,附带参数,实际调用dispatchEvent
  6. void dispatchCustomEvent(const std::string &eventName, void *optionalUserData = nullptr);
  7. protected:
  8. // touch事件要另外处理,因为有ALL_AT_ONCE and ONE_BY_NONE mode
  9. void dispatchTouchEvent(EventTouch* event);
  10. /** Dispatches event to listeners with a specified listener type */
  11. void dispatchEventToListeners(EventListenerVector* listeners,
  12. const std::function<bool(EventListener*)>& onEvent);
  13. /** Special version dispatchEventToListeners for touch/mouse event.
  14. *
  15. * Touch/mouse event process flow different with common event,
  16. * for scene graph node listeners, touch event process flow should
  17. * order by viewport/camera first, because the touch location convert
  18. * to 3D world space is different by different camera.
  19. * When listener process touch event, can get current camera by Camera::getVisitingCamera().
  20. */
  21. void dispatchTouchEventToListeners(EventListenerVector* listeners,
  22. const std::function<bool(EventListener*)>& onEvent);
  23. }

EventLisenter的存储

通过EventDispatcher如何存储EventListener,基本可以了解到EventDispatcher的工作原理。最核心的是_listenerMap成员:

  1. class CC_DLL EventDispatcher : public Ref {
  2. std::unordered_map<EventListener::ListenerID, EventListenerVector*> _listenerMap;
  3. // 下面的_nodeListenersMap、_nodePriorityMap、_globalZOrderNodeMap都是为了
  4. // _listenerMap中的sceneGraphListener的排序
  5. // listener.getAssociatedNode() <-> listeners
  6. // 所有关联相同node的listener保存在同一个vector中
  7. std::unordered_map<Node*, std::vector<EventListener*>*> _nodeListenersMap;
  8. // 假设已关联node的绘制顺序是:node1,node2,node3,node4
  9. // 则_nodePriorityMap = {
  10. // node1 : 0,
  11. // node2 : 1,
  12. // node3 : 2,
  13. // node4 : 3,
  14. // }
  15. // 主要用于_listenerMap中的sceneGraphListener的排序,绘制顺序的反序排列
  16. std::unordered_map<Node*, int> _nodePriorityMap;
  17. // key: Global Z Order, value: Sorted Nodes
  18. // 有相同globalzorder的node保存在同一个vector中,已按绘制顺序排好序
  19. // 这些node都在_nodeListenersMap中。
  20. std::unordered_map<float, std::vector<Node*>> _globalZOrderNodeMap;
  21. }

点击查看【processon】
通过上图可以很直观地了解到EventListener是如何被存储组织的了。

注册/注销原理

如果当前正在派发事件,EventListener的注册/注销并不是立即执行,而是加入了等待队列,在一个派发事件结束时检查是否还在派发事件,如果没有了才注册/注销等待队列中的Listener。
点击查看【processon】

  1. class EventDispatcher : public Ref{
  2. // 待注册的eventListener的等待队列,需要等到dispatch event结束之后加入。
  3. std::vector<EventListener*> _toAddedListeners;
  4. // 待注销的eventListener的等待队列,需要等到dispatch event结束之后加入。
  5. std::vector<EventListener*> _toRemovedListeners;
  6. // 当前正在派发事件的数量。
  7. int _inDispatch;
  8. }
  9. void EventDispatcher::addEventListener(EventListener* listener) {
  10. if (_inDispatch == 0){
  11. forceAddEventListener(listener); //当前没有派发的事件,就立即注册
  12. }
  13. else {
  14. _toAddedListeners.push_back(listener); //当前正在派发事件,listener加入等待队列。
  15. }
  16. ......
  17. }
  18. //注册立即生效,即加入到_listenerMap中。
  19. void EventDispatcher::forceAddEventListener(EventListener* listener) {
  20. EventListenerVector* listeners = nullptr; //
  21. EventListener::ListenerID listenerID = listener->getListenerID();
  22. auto itr = _listenerMap.find(listenerID);
  23. if (itr == _listenerMap.end()) {
  24. listeners = new (std::nothrow) EventListenerVector();
  25. _listenerMap.emplace(listenerID, listeners);
  26. }
  27. else {
  28. listeners = itr->second;
  29. }
  30. //插入到EventListenerVector中,见上图的_listenerMap结构
  31. listeners->push_back(listener);
  32. ......
  33. }
  34. void EventDispatcher::dispatchEvent(Event* event)
  35. {
  36. ......
  37. // 利用构造、析构函数,巧妙的记录当前派发数量
  38. DispatchGuard guard(_inDispatch); // 构造时,_inDispatch + 1
  39. // 析构时,_inDispatch - 1
  40. ......
  41. sortEventListeners(__getListenerID(event)); // 在派发的时候再排序,提高性能
  42. ...... // 在这里触发监听回调
  43. updateListeners(event); // 处理注册/注销的等待队列
  44. } // 跳出函数栈,
  45. // 处理注册/注销的等待队列
  46. void EventDispatcher::updateListeners(Event* event){
  47. ......
  48. if (!_toAddedListeners.empty()) {
  49. for (auto& listener : _toAddedListeners) {
  50. forceAddEventListener(listener);
  51. }
  52. _toAddedListeners.clear();
  53. }
  54. if (!_toRemovedListeners.empty()) {
  55. cleanToRemovedListeners(); //立即注销
  56. }
  57. }
  58. // 巧妙地通过自动变量自动执行构造析构来记录正在派发的数量。
  59. class DispatchGuard {
  60. public:
  61. DispatchGuard(int& count):_count(count){
  62. ++_count;
  63. }
  64. ~DispatchGuard(){
  65. --_count;
  66. }
  67. };

EventListener的排序

EventListener的优先级、关联Node的zOrder变动时,都需要对_listenerMap中的Listener进行重排。

会导致Listener重排的操作
操作 SceneGraphPriority FixedPriority
setLocalZOrder
setGlobalZOrder
setPriority
forceAddEventListener
removeEventListener

在发生变动时,cocos会对受影响的ListenerID进行标记,然后在dispatchEvent时,如果需要派发事件的ListenerID有标记,再对这个ListenerID进行排序,这样做可以减少排序,提供性能。

  1. class CC_DLL EventDispatcher : public Ref{
  2. protected:
  3. // Priority dirty flag
  4. enum class DirtyFlag {
  5. NONE = 0,
  6. FIXED_PRIORITY = 1 << 0,
  7. SCENE_GRAPH_PRIORITY = 1 << 1,
  8. ALL = FIXED_PRIORITY | SCENE_GRAPH_PRIORITY
  9. };
  10. // 被标记了的ListenerID
  11. std::unordered_map<EventListener::ListenerID, DirtyFlag> _priorityDirtyFlagMap;
  12. /** The nodes were associated with scene graph based priority listeners */
  13. std::set<Node*> _dirtyNodes;
  14. }
  15. // 比如在node改变zOrder时被标记,在setLocalZOrder方法中调用。
  16. void EventDispatcher::setDirtyForNode(Node* node) {
  17. // Mark the node dirty only when there is an eventlistener associated with it.
  18. if (_nodeListenersMap.find(node) != _nodeListenersMap.end()) {
  19. _dirtyNodes.insert(node);
  20. }
  21. // Also set the dirty flag for node's children
  22. const auto& children = node->getChildren();
  23. for (const auto& child : children) {
  24. setDirtyForNode(child);
  25. }
  26. }
  27. // 在派发事件时才对需要用到的listenerID且被标记了才进行排序
  28. void EventDispatcher::dispatchEvent(Event* event) {
  29. ......
  30. if (event->getType() == Event::Type::TOUCH) {
  31. dispatchTouchEvent(static_cast<EventTouch*>(event));
  32. return;
  33. }
  34. sortEventListeners(listenerID);
  35. ......
  36. }
  37. void EventDispatcher::dispatchTouchEvent(EventTouch* event)
  38. {
  39. //同理
  40. sortEventListeners(EventListenerTouchOneByOne::LISTENER_ID);
  41. sortEventListeners(EventListenerTouchAllAtOnce::LISTENER_ID);
  42. ......
  43. }
  44. void EventDispatcher::sortEventListeners(const EventListener::ListenerID& listenerID) {
  45. DirtyFlag dirtyFlag = DirtyFlag::NONE;
  46. auto dirtyIter = _priorityDirtyFlagMap.find(listenerID);
  47. if (dirtyIter != _priorityDirtyFlagMap.end()) {
  48. dirtyFlag = dirtyIter->second;
  49. }
  50. // 只有被标记了,才进行排序。
  51. if (dirtyFlag != DirtyFlag::NONE) {
  52. // Clear the dirty flag first, if `rootNode` is nullptr,
  53. // then set its dirty flag of scene graph priority
  54. dirtyIter->second = DirtyFlag::NONE;
  55. if ((int)dirtyFlag & (int)DirtyFlag::FIXED_PRIORITY){
  56. sortEventListenersOfFixedPriority(listenerID);
  57. }
  58. if ((int)dirtyFlag & (int)DirtyFlag::SCENE_GRAPH_PRIORITY){
  59. auto rootNode = Director::getInstance()->getRunningScene();
  60. if (rootNode){
  61. sortEventListenersOfSceneGraphPriority(listenerID, rootNode);
  62. }
  63. else{
  64. dirtyIter->second = DirtyFlag::SCENE_GRAPH_PRIORITY;
  65. }
  66. }
  67. }
  68. }