一、使用示例
EventCustom:自定义事件
std::string eventName("自定义事件"); // event name
EventCustom event(eventName); // event
void *pData = ......; // event data
EventListenerCustom *pListener = nullptr; // event listener
auto listenerPriority = 1; // listener priority
std::function<void(EventCustom*)> callback = nullptr; // event callback
auto dispatcher = Director::getInstance()->getEventDispatcher(); // event dispatcher
callback = [](EventCustom* event){
char* buf = static_cast<char*>(event->getUserData());
}
pListener = EventListenerCustom::create(eventName, callback);
event.setUserData(pData);
// register listener
dispatcher->addEventListenerWithFixedPriority(pListener, listenerPriority);
// dispatch event
_eventDispatcher->dispatchEvent(&pEvent);
// unregister listener
dispatcher->removeEventListener(pListener);
TouchOneByOne:单点触摸事件
// Adds Touch Event Listener
auto 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传入target
Node* getCurrentTarget() { return _currentTarget; }
};
我们再学习一下,最常用的两个事件,EventTouch和EventCustom。
<a name="GbNze"></a>
## EventTouch
触摸事件。
```cpp
class 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 coordinates
Vec2 getDelta() const;
//Get touch id,touch唯一标识
int getID();
};
EventCustom
自定义事件,像EventTouch、EventMouse这些都是从硬件产生的事件,我们也可以通过EventCustom来产生自定义的事件。
部分源码如下:
class CC_DLL EventCustom : public Event
{
EventCustom(const std::string& eventName); // 事件名称,类似ID,唯一区分EventCustom
void setUserData(void* data); // 传递参数给listener
void* 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 reset
const 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才可能接收事件,默认true
bool 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是0
void setFixedPriority(int fixedPriority) { _fixedPriority = fixedPriority; }
int getFixedPriority() const { return _fixedPriority; }
/** Sets the node associated with this listener */
//scene graph priority listeners注册时,绑定的Node
void 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
单点触摸。
```cpp
class 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 = 0
void 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 {
// 删除具体的listener
void removeEventListener(EventListener* listener);
// 删除指定Type的listener
void removeEventListenersForType(EventListener::Type listenerType);
// 删除所有关联node的listener,还可以递归删除node子节点关联的listener。
void removeEventListenersForTarget(Node* target, bool recursive = false);
// 按listenerID删除自定义listener
void removeCustomEventListeners(const std::string& customEventName);
// 删除所有listener
void removeAllEventListeners();
}
暂停/恢复监听
class EventListener : public Ref{
public:
// setEnabled(false),listener将不会接收到任何事件
// setEnabled(true),恢复接收事件
// 默认为true
void setEnabled(bool enabled);
protected:
// 只用于scene graph priority listeners,针对Node会有onEnter、onExit情况
// fixed priority listeners的paused总为false
void 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 默认为true
l->isRegistered() && // 这个由于eventdispatcher内部调用setRegistered决定
onEvent(l) // lisenter callback
){
......
}
分发事件
class EventDispatcher : public Ref{
public:
// 派发事件,会触发updateListeners,处理toRemoved、toAdded。
void dispatchEvent(Event* event);
// 派发自定义事件,附带参数,实际调用dispatchEvent
void dispatchCustomEvent(const std::string &eventName, void *optionalUserData = nullptr);
protected:
// touch事件要另外处理,因为有ALL_AT_ONCE and ONE_BY_NONE mode
void 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 flag
enum class DirtyFlag {
NONE = 0,
FIXED_PRIORITY = 1 << 0,
SCENE_GRAPH_PRIORITY = 1 << 1,
ALL = FIXED_PRIORITY | SCENE_GRAPH_PRIORITY
};
// 被标记了的ListenerID
std::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 children
const 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 priority
dirtyIter->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;
}
}
}
}