一、使用示例
EventCustom:自定义事件
std::string eventName("自定义事件"); // event nameEventCustom event(eventName); // eventvoid *pData = ......; // event dataEventListenerCustom *pListener = nullptr; // event listenerauto listenerPriority = 1; // listener prioritystd::function<void(EventCustom*)> callback = nullptr; // event callbackauto dispatcher = Director::getInstance()->getEventDispatcher(); // event dispatchercallback = [](EventCustom* event){char* buf = static_cast<char*>(event->getUserData());}pListener = EventListenerCustom::create(eventName, callback);event.setUserData(pData);// register listenerdispatcher->addEventListenerWithFixedPriority(pListener, listenerPriority);// dispatch event_eventDispatcher->dispatchEvent(&pEvent);// unregister listenerdispatcher->removeEventListener(pListener);
TouchOneByOne:单点触摸事件
// Adds Touch Event Listenerauto listener = EventListenerTouchOneByOne::create();listener->setSwallowTouches(true);listener->onTouchBegan = CC_CALLBACK_2(Box2DView::onTouchBegan, this);listener->onTouchMoved = CC_CALLBACK_2(Box2DView::onTouchMoved, this);listener->onTouchEnded = CC_CALLBACK_2(Box2DView::onTouchEnded, this);_eventDispatcher->addEventListenerWithFixedPriority(listener, -10);bool Box2DView::onTouchBegan(Touch* touch, Event* event){// 世界坐标转换为本地坐标auto localPos = convertToNodeSpace( touch->getLocation() );return true; // 接收后续的touch,并且吞噬touch,后续listener不会再接收到。}void Box2DView::onTouchMoved(Touch* touch, Event* event) { }void Box2DView::onTouchEnded(Touch* touch, Event* event) { }
TouchAllAtOnce:多点触摸事件
auto listener = EventListenerTouchAllAtOnce::create();listener->onTouchesBegan = CC_CALLBACK_2(ForceTouchTest::onTouchesBegan, this);listener->onTouchesMoved = CC_CALLBACK_2(ForceTouchTest::onTouchesMoved, this);listener->onTouchesEnded = CC_CALLBACK_2(ForceTouchTest::onTouchesEnded, this);_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);void ForceTouchTest::onTouchesBegan(const std::vector<cocos2d::Touch*>& touches,cocos2d::Event* event) { }void ForceTouchTest::onTouchesMoved(const std::vector<cocos2d::Touch*>& touches,cocos2d::Event* event) {for(auto& t : touches) { }}void ForceTouchTest::onTouchesEnded(const std::vector<cocos2d::Touch*>& touches,cocos2d::Event* event) { }
EventKeyBoard:键盘事件
auto keyboardListener = EventListenerKeyboard::create();keyboardListener->onKeyPressed = CC_CALLBACK_2(Box2DView::onKeyPressed, this);keyboardListener->onKeyReleased = CC_CALLBACK_2(Box2DView::onKeyReleased, this);_eventDispatcher->addEventListenerWithFixedPriority(keyboardListener, -11);void Box2DView::onKeyPressed(EventKeyboard::KeyCode code, Event* event){static_cast<unsigned char>(code)}void Box2DView::onKeyReleased(EventKeyboard::KeyCode code, Event* event){static_cast<unsigned char>(code)}
以上例子展示了一个事件系统的基本组成元素和其功能。
- Event:事件
- name:事件名称
- data:事件的数据
- EventListener:事件监听者
- callback:监听回调
- event name:监听的事件名称
EventDispatcher:事件分发者
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(); // 事件类型,枚举
void stopPropagation(); // 本事件停止“传播”,EventDispatcher不会再分发此事件给后续的监听者。bool isStopped(); // 是否已停止“传播”// 事件关联的target// 当且仅当addEventListenerWithSceneGraphPriority时传入的node,否则为nullptr// 当然你可以通过继承Event通过setCurrentTarget传入targetNode* getCurrentTarget() { return _currentTarget; }
};
我们再学习一下,最常用的两个事件,EventTouch和EventCustom。<a name="GbNze"></a>## EventTouch触摸事件。```cppclass CC_DLL EventTouch : public Event {public:enum class EventCode {BEGAN, // 手指按下屏幕,触摸开始MOVED, // 手指滑动屏幕ENDED, // 手指离开屏幕CANCELLED // 触摸被动取消,比如滑动屏幕时,来电、信息等紧急事件};EventCode getEventCode(); // 触摸点的状态// 每个touch记录了当前屏幕上处于相同状态的一组触摸点的集合。// 比如一组滑动的触摸事件,代表一次滑动const std::vector<Touch*>& getTouches() const { return _touches; }};
Touch
触摸信息。
class CC_DLL Touch : public Ref {public:enum class DispatchMode { // how the touches are dispatched.ALL_AT_ONCE, /** All at once. */ONE_BY_ONE, /** One by one. */};Vec2 getLocation() const; // 返回当前触摸的世界坐标Vec2 getLocationInView() const; // 返回当前触摸的屏幕坐标Vec2 getPreviousLocation() const; // 返回上一个触摸的世界坐标Vec2 getPreviousLocationInView() const; // 返回上一个触摸的屏幕坐标Vec2 getStartLocation() const; // 返回触摸起始点的世界坐标Vec2 getStartLocationInView() const; // 返回触摸起始点的屏幕坐标//Returns the delta of 2 current touches locations in screen coordinatesVec2 getDelta() const;//Get touch id,touch唯一标识int getID();};
EventCustom
自定义事件,像EventTouch、EventMouse这些都是从硬件产生的事件,我们也可以通过EventCustom来产生自定义的事件。
部分源码如下:
class CC_DLL EventCustom : public Event{EventCustom(const std::string& eventName); // 事件名称,类似ID,唯一区分EventCustomvoid setUserData(void* data); // 传递参数给listenervoid* getUserData();const std::string& getEventName();};
下面是引擎内部自定义的一些事件。
// director->setNextScene前后。const char *Director::EVENT_BEFORE_SET_NEXT_SCENE = "director_before_set_next_scene";const char *Director::EVENT_AFTER_SET_NEXT_SCENE = "director_after_set_next_scene";// director->setProjection,投影矩阵发生变化const char *Director::EVENT_PROJECTION_CHANGED = "director_projection_changed";// draw指的是:UI树遍历 + 绘制渲染。const char *Director::EVENT_BEFORE_DRAW = "director_before_draw";const char *Director::EVENT_AFTER_DRAW = "director_after_draw";// UI树遍历后const char *Director::EVENT_AFTER_VISIT = "director_after_visit";// shcedule update前后const char *Director::EVENT_BEFORE_UPDATE = "director_before_update";const char *Director::EVENT_AFTER_UPDATE = "director_after_update";// director resetconst char *Director::EVENT_RESET = "director_reset";// The application will come to foreground.// This message is posted in cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxRenderer.cpp.#define EVENT_COME_TO_FOREGROUND "event_come_to_foreground"// The renderer[android:GLSurfaceView.Renderer WP8:Cocos2dRenderer] was recreated.// This message is used for reloading resources before renderer is recreated on Android/WP8.// This message is posted in cocos/platform/android/javaactivity.cpp and cocos\platform\wp8-xaml\cpp\Cocos2dRenderer.cpp.#define EVENT_RENDERER_RECREATED "event_renderer_recreated"// The application will come to background.// This message is used for doing something before coming to background, such as save RenderTexture.// This message is posted in cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxRenderer.cpp and cocos\platform\wp8-xaml\cpp\Cocos2dRenderer.cpp.#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; // 复制
void setEnabled(bool enabled); // true才可能接收事件,默认truebool isEnabled();
protected: // 只用于scene graph priority listeners,针对Node会有onEnter、onExit情况 // fixed priority listeners的paused总为false void setPaused(bool paused); bool isPaused();
// 是否注册到eventdispatcher中。void setRegistered(bool registered);bool isRegistered();//只针对fixed priority listeners,不能设置为0,因为scene graph priority是0void setFixedPriority(int fixedPriority) { _fixedPriority = fixedPriority; }int getFixedPriority() const { return _fixedPriority; }/** Sets the node associated with this listener *///scene graph priority listeners注册时,绑定的Nodevoid setAssociatedNode(Node* node) { _node = node; }Node* getAssociatedNode() const { return _node; }
};
到这里我们发现有好几个type、ID好相近,EventType、EventListenerType、EventListenerID,我们来总结一下它们的关系:| Event::Type<br />(枚举) | EventListener::Type<br />(枚举) | EventListenerID<br />(string) || :---: | :---: | :---: || TOUCH | TOUCH_ONE_BY_ONE | __cc_touch_one_by_one || | TOUCH_ALL_AT_ONCE | __cc_touch_all_at_once || KEYBOARD | KEYBOARD | __cc_keyboard || ACCELERATION | ACCELERATION | __cc_acceleration || MOUSE | MOUSE | __cc_mouse || FOCUS | FOCUS | __cc_focus_event || GAME_CONTROLLER | GAME_CONTROLLER | __cc_controller || CUSTOM | CUSTOM | getEventName() |**Touch为什么不是一一对应?**<br />一个touch事件会携带当前屏幕上的所有触摸点的触摸信息,保存在一个vector中,TOUCH_ONE_BY_ONE的listenter对于vector中每个touch都会回调一次,即一次处理一个触摸信息,TOUCH_ALL_AT_ONCE的listener只回调一次,传入的参数就是vector,一次处理当前触摸事件中的所有触摸点的触摸信息。**为什么还要一个ListenerID?**<br />既然都有一个EventListener Type了,还有ListenerID干嘛?<br />一个EventType可能对应多个EventListenerType,而一个EventListenerType可能也对应多个EventListenerID,比如EventListnerCustom,每个eventname就是一个listenerID。可以这么理解,EventListenerType是划分大类,EventlistenerID是划分细类,ListenerID才是真正决定了该Listener接收具体哪个Event。<a name="U7LhT"></a>## EventListenerTouchOneByOne单点触摸。```cppclass CC_DLL EventListenerTouchOneByOne : public EventListener{public:static const std::string LISTENER_ID; // __cc_touch_one_by_one// LISTENER_ID是固定的__cc_touch_one_by_one,所以create时不需要这个参数了。static EventListenerTouchOneByOne* create();// 是否阻止触摸touch事件向后续listener分发,ontouchbegan必须返回true才能生效// 也就是说自己必须处理这些touch事件。void setSwallowTouches(bool needSwallow);bool isSwallowTouches();public:// 注意到touchBegan返回了一个bool值,告诉eventdispatcher是否应该将该触摸点的// 后续触摸数据传递给这个listener// 单点触摸最好加上状态判断,防止连续点击,多点点击导致touch回调被执行多次产生的潜在bug// 绑定Node节点的可见性不会影响EventDispatcher对该listener的事件分发,所以// 我们也要考虑可见性的逻辑判断typedef std::function<bool(Touch*, Event*)> ccTouchBeganCallback;typedef std::function<void(Touch*, Event*)> ccTouchCallback;ccTouchBeganCallback onTouchBegan;ccTouchCallback onTouchMoved;ccTouchCallback onTouchEnded;ccTouchCallback onTouchCancelled;// onTouchBegan例子bool Node::onTouchBegan(Touch* touch, Event* event){// isHandling: 是否正在处理触摸,防止多个触摸点或者连续点击,连续重复触发touch回调。// isVisble: eventListener绑定节点的可见性,不可见应该不需要触发touch回调。if(!isHandling || !isVisble){return false;}//绑定节点的父节点不可见,也不应该触发touch回调。for(auto parent = getParent(); parent != nullptr; parent = parent->getParent()){if(!parent->isVisble()){return false;}}isHandling = true;}};
EventListenerTouchOneByOne的swallowTouches和onTouchBegan的影响
| TouchID | onTouchBegan | swallowTouches | OneByOne | AllAtOnce |
|---|---|---|---|---|
| 1 | true | true | 调用 | 无调用 |
| 2 | true | false | 调用 | 调用 |
| 3 | false | true | 无调用 | 调用 |
| 4 | false | false | 无调用 | 调用 |
EventListenerTouchAllAtOnce
多点触摸。
class CC_DLL EventListenerTouchAllAtOnce : public EventListener {public:static const std::string LISTENER_ID; // __cc_touch_all_at_once// LISTENER_ID是固定的,所以create时不需要这个参数了。static EventListenerTouchAllAtOnce* create();public:// 可以看到,相比于EventListenerTouchOneByOne,这个传入是touch的vector// 表示当前屏幕上接收到的所有触摸数据。typedef std::function<void(const std::vector<Touch*>&, Event*)> ccTouchesCallback;// 触摸回调是public类型的,直接赋值设置。ccTouchesCallback onTouchesBegan;ccTouchesCallback onTouchesMoved;ccTouchesCallback onTouchesEnded;ccTouchesCallback onTouchesCancelled;};
每次的触摸事件,EventDispatcher会将每个触摸点单独作用在每个EventListenerTouchOneByOne,然后再将所有的触摸点集合作用在EventListenerTouchAllAtOnce上,如果允许的话。
EventListenerCustom
class CC_DLL EventListenerCustom : public EventListener {public:static EventListenerCustom* create(const std::string& eventName,const std::function<void(EventCustom*)>& callback);// 决定了diaptcher.addEventListener能否成功virtual bool checkAvailable() override;virtual EventListenerCustom* clone() override; // 复制protected:std::function<void(EventCustom*)> _onCustomEvent;friend class LuaEventListenerCustom;};
四、事件分发
API
注册监听
class CC_DLL EventDispatcher : public Ref {public:// 注册SceneGraphListener// 按node在UI树中的遍历顺序来决定优先级,越先绘制,Listener优先级越低。void addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node);// 注册FixedPriorityListener,即指定优先级的listener// fixedPriority: listener的优先级,固定数值优先级的,数值越小,优先级越高// 不能为0,SceneGraphListener默认fixedPriority = 0void addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority);// 注册自定义的事件监听,本质上是:// addEventListenerWithFixedPriority(listener, 1)EventListenerCustom* addCustomEventListener(const std::string& eventName,const std::function<void(EventCustom*)>& callback );}
注销监听
class CC_DLL EventDispatcher : public Ref {// 删除具体的listenervoid removeEventListener(EventListener* listener);// 删除指定Type的listenervoid removeEventListenersForType(EventListener::Type listenerType);// 删除所有关联node的listener,还可以递归删除node子节点关联的listener。void removeEventListenersForTarget(Node* target, bool recursive = false);// 按listenerID删除自定义listenervoid removeCustomEventListeners(const std::string& customEventName);// 删除所有listenervoid removeAllEventListeners();}
暂停/恢复监听
class EventListener : public Ref{public:// setEnabled(false),listener将不会接收到任何事件// setEnabled(true),恢复接收事件// 默认为truevoid setEnabled(bool enabled);protected:// 只用于scene graph priority listeners,针对Node会有onEnter、onExit情况// fixed priority listeners的paused总为falsevoid setPaused(bool paused);// 是否注册到Dispatcher中,由dispatcher调用void setRegistered(bool registered);}class EventDispatcher : public Ref{// 用于通过addEventListenerWithSceneGraphPriority注册的Listener// 这些listener// 在node onExit()的时候暂停接收事件// 在node onEnter()的时候恢复接收事件// 实际调用的是上面EventListener的setPause()void resumeEventListenersForTarget(Node* target, bool recursive = false)void pauseEventListenersForTarget(Node* target, bool recursive = false)}// 感觉EventListener有很多个状态开关,我们捋一下:// 在EventDispatcher中,要使一个EventListener能回调的条件如下;if (l->isEnabled() && // 首先必须Enable,这个可由外部调用决定!l->isPaused() && // 必须not pause,这个是针对scene graph priority listeners// 上面的pause、resume可以决定// fixed priority 默认为truel->isRegistered() && // 这个由于eventdispatcher内部调用setRegistered决定onEvent(l) // lisenter callback){......}
分发事件
class EventDispatcher : public Ref{public:// 派发事件,会触发updateListeners,处理toRemoved、toAdded。void dispatchEvent(Event* event);// 派发自定义事件,附带参数,实际调用dispatchEventvoid dispatchCustomEvent(const std::string &eventName, void *optionalUserData = nullptr);protected:// touch事件要另外处理,因为有ALL_AT_ONCE and ONE_BY_NONE modevoid dispatchTouchEvent(EventTouch* event);/** Dispatches event to listeners with a specified listener type */void dispatchEventToListeners(EventListenerVector* listeners,const std::function<bool(EventListener*)>& onEvent);/** Special version dispatchEventToListeners for touch/mouse event.** Touch/mouse event process flow different with common event,* for scene graph node listeners, touch event process flow should* order by viewport/camera first, because the touch location convert* to 3D world space is different by different camera.* When listener process touch event, can get current camera by Camera::getVisitingCamera().*/void dispatchTouchEventToListeners(EventListenerVector* listeners,const std::function<bool(EventListener*)>& onEvent);}
EventLisenter的存储
通过EventDispatcher如何存储EventListener,基本可以了解到EventDispatcher的工作原理。最核心的是_listenerMap成员:
class CC_DLL EventDispatcher : public Ref {std::unordered_map<EventListener::ListenerID, EventListenerVector*> _listenerMap;// 下面的_nodeListenersMap、_nodePriorityMap、_globalZOrderNodeMap都是为了// _listenerMap中的sceneGraphListener的排序// listener.getAssociatedNode() <-> listeners// 所有关联相同node的listener保存在同一个vector中std::unordered_map<Node*, std::vector<EventListener*>*> _nodeListenersMap;// 假设已关联node的绘制顺序是:node1,node2,node3,node4// 则_nodePriorityMap = {// node1 : 0,// node2 : 1,// node3 : 2,// node4 : 3,// }// 主要用于_listenerMap中的sceneGraphListener的排序,绘制顺序的反序排列std::unordered_map<Node*, int> _nodePriorityMap;// key: Global Z Order, value: Sorted Nodes// 有相同globalzorder的node保存在同一个vector中,已按绘制顺序排好序// 这些node都在_nodeListenersMap中。std::unordered_map<float, std::vector<Node*>> _globalZOrderNodeMap;}
点击查看【processon】
通过上图可以很直观地了解到EventListener是如何被存储组织的了。
注册/注销原理
如果当前正在派发事件,EventListener的注册/注销并不是立即执行,而是加入了等待队列,在一个派发事件结束时检查是否还在派发事件,如果没有了才注册/注销等待队列中的Listener。
点击查看【processon】
class EventDispatcher : public Ref{// 待注册的eventListener的等待队列,需要等到dispatch event结束之后加入。std::vector<EventListener*> _toAddedListeners;// 待注销的eventListener的等待队列,需要等到dispatch event结束之后加入。std::vector<EventListener*> _toRemovedListeners;// 当前正在派发事件的数量。int _inDispatch;}void EventDispatcher::addEventListener(EventListener* listener) {if (_inDispatch == 0){forceAddEventListener(listener); //当前没有派发的事件,就立即注册}else {_toAddedListeners.push_back(listener); //当前正在派发事件,listener加入等待队列。}......}//注册立即生效,即加入到_listenerMap中。void EventDispatcher::forceAddEventListener(EventListener* listener) {EventListenerVector* listeners = nullptr; //EventListener::ListenerID listenerID = listener->getListenerID();auto itr = _listenerMap.find(listenerID);if (itr == _listenerMap.end()) {listeners = new (std::nothrow) EventListenerVector();_listenerMap.emplace(listenerID, listeners);}else {listeners = itr->second;}//插入到EventListenerVector中,见上图的_listenerMap结构listeners->push_back(listener);......}void EventDispatcher::dispatchEvent(Event* event){......// 利用构造、析构函数,巧妙的记录当前派发数量DispatchGuard guard(_inDispatch); // 构造时,_inDispatch + 1// 析构时,_inDispatch - 1......sortEventListeners(__getListenerID(event)); // 在派发的时候再排序,提高性能...... // 在这里触发监听回调updateListeners(event); // 处理注册/注销的等待队列} // 跳出函数栈,// 处理注册/注销的等待队列void EventDispatcher::updateListeners(Event* event){......if (!_toAddedListeners.empty()) {for (auto& listener : _toAddedListeners) {forceAddEventListener(listener);}_toAddedListeners.clear();}if (!_toRemovedListeners.empty()) {cleanToRemovedListeners(); //立即注销}}// 巧妙地通过自动变量自动执行构造析构来记录正在派发的数量。class DispatchGuard {public:DispatchGuard(int& count):_count(count){++_count;}~DispatchGuard(){--_count;}};
EventListener的排序
EventListener的优先级、关联Node的zOrder变动时,都需要对_listenerMap中的Listener进行重排。
| 会导致Listener重排的操作 | ||
|---|---|---|
| 操作 | SceneGraphPriority | FixedPriority |
| setLocalZOrder | √ | |
| setGlobalZOrder | √ | |
| setPriority | √ | |
| forceAddEventListener | √ | √ |
| removeEventListener | √ | √ |
在发生变动时,cocos会对受影响的ListenerID进行标记,然后在dispatchEvent时,如果需要派发事件的ListenerID有标记,再对这个ListenerID进行排序,这样做可以减少排序,提供性能。
class CC_DLL EventDispatcher : public Ref{protected:// Priority dirty flagenum class DirtyFlag {NONE = 0,FIXED_PRIORITY = 1 << 0,SCENE_GRAPH_PRIORITY = 1 << 1,ALL = FIXED_PRIORITY | SCENE_GRAPH_PRIORITY};// 被标记了的ListenerIDstd::unordered_map<EventListener::ListenerID, DirtyFlag> _priorityDirtyFlagMap;/** The nodes were associated with scene graph based priority listeners */std::set<Node*> _dirtyNodes;}// 比如在node改变zOrder时被标记,在setLocalZOrder方法中调用。void EventDispatcher::setDirtyForNode(Node* node) {// Mark the node dirty only when there is an eventlistener associated with it.if (_nodeListenersMap.find(node) != _nodeListenersMap.end()) {_dirtyNodes.insert(node);}// Also set the dirty flag for node's childrenconst auto& children = node->getChildren();for (const auto& child : children) {setDirtyForNode(child);}}// 在派发事件时才对需要用到的listenerID且被标记了才进行排序void EventDispatcher::dispatchEvent(Event* event) {......if (event->getType() == Event::Type::TOUCH) {dispatchTouchEvent(static_cast<EventTouch*>(event));return;}sortEventListeners(listenerID);......}void EventDispatcher::dispatchTouchEvent(EventTouch* event){//同理sortEventListeners(EventListenerTouchOneByOne::LISTENER_ID);sortEventListeners(EventListenerTouchAllAtOnce::LISTENER_ID);......}void EventDispatcher::sortEventListeners(const EventListener::ListenerID& listenerID) {DirtyFlag dirtyFlag = DirtyFlag::NONE;auto dirtyIter = _priorityDirtyFlagMap.find(listenerID);if (dirtyIter != _priorityDirtyFlagMap.end()) {dirtyFlag = dirtyIter->second;}// 只有被标记了,才进行排序。if (dirtyFlag != DirtyFlag::NONE) {// Clear the dirty flag first, if `rootNode` is nullptr,// then set its dirty flag of scene graph prioritydirtyIter->second = DirtyFlag::NONE;if ((int)dirtyFlag & (int)DirtyFlag::FIXED_PRIORITY){sortEventListenersOfFixedPriority(listenerID);}if ((int)dirtyFlag & (int)DirtyFlag::SCENE_GRAPH_PRIORITY){auto rootNode = Director::getInstance()->getRunningScene();if (rootNode){sortEventListenersOfSceneGraphPriority(listenerID, rootNode);}else{dirtyIter->second = DirtyFlag::SCENE_GRAPH_PRIORITY;}}}}
