cocos2d-x版本:3.17.2
运行环境:Visual Studio 2017
解决方案配置:Debug Win32
事件分发主要由三部分组成:
三者的关系可以理解为:在应用程序中事件可以看作是一个带有数据的实体,而事件监听器就是事情发生之后做一些处理工作的人;派发事件就是将事件这个实体派发出去,告诉大家发生了这个事情,然后事件监听器会监听到事件触发,接收事件这个实体,取到事件中的数据,做相应的处理。
事件的基类Event
class CC_DLL Event : public Ref
{
public:
/** Type Event type.*/
enum class Type
{
TOUCH,
KEYBOARD,
ACCELERATION,
MOUSE,
FOCUS,
GAME_CONTROLLER,
CUSTOM
};
CC_CONSTRUCTOR_ACCESS:
/** Constructor */
Event(Type type);
public:
virtual ~Event();
Type getType() const { return _type; }
// 停止事件传播
void stopPropagation() { _isStopped = true; }
// 判断事件是否停止
bool isStopped() const { return _isStopped; }
// 获取当前事件的目标
Node* getCurrentTarget() { return _currentTarget; }
protected:
// 设置当前的目标
void setCurrentTarget(Node* target) { _currentTarget = target; }
// 事件的类型
Type _type;
// 是否停止标志
bool _isStopped;
// 事件附加的目标结点
Node* _currentTarget;
// 友元类,事件分发器
friend class EventDispatcher;
};
Event中的enum声明了7种事件类型,分别是触摸事件、键盘事件、加速计事件、鼠标事件、焦点事件、手柄事件和自定义事件,这些事件继承Event类,再额外添加自己相应的一些属性。
例如,这里的键盘事件EventKeyboard 就多定义了两个属性,_keyCode 表示按下的键值,_isPressed 表示是按下还是松开
class CC_DLL EventKeyboard : public Event
{
public:
// function
protected:
// 按下的键值
KeyCode _keyCode;
// 按下还是松开
bool _isPressed;
friend class EventListenerKeyboard;
};
该类负责监听事件的发生,并调用相应的回调函数来处理事件。
class CC_DLL EventListener : public Ref
{
public:
/** Type Event type.*/
enum class Type
{
UNKNOWN,
TOUCH_ONE_BY_ONE,
TOUCH_ALL_AT_ONCE,
KEYBOARD,
MOUSE,
ACCELERATION,
FOCUS,
GAME_CONTROLLER,
CUSTOM
};
typedef std::string ListenerID;
CC_CONSTRUCTOR_ACCESS:
EventListener();
// 初始化
bool init(Type t, const ListenerID& listenerID, const std::function<void(Event*)>& callback);
public:
virtual ~EventListener();
// 检查当前监听器是否可用
virtual bool checkAvailable() = 0;
// 复制当前监听器
virtual EventListener* clone() = 0;
// 设置当前监听器的启用或禁用
void setEnabled(bool enabled) { _isEnabled = enabled; }
// 检查是否启用了侦听器
bool isEnabled() const { return _isEnabled; }
protected:
// 为监听器设置暂停状态
void setPaused(bool paused) { _paused = paused; }
// 判断监听器是否为暂停状态
bool isPaused() const { return _paused; }
// 标记该监听器被EventDispatcher注册
void setRegistered(bool registered) { _isRegistered = registered; }
// 检查该监听器是否被EventDispatcher注册
bool isRegistered() const { return _isRegistered; }
// 返回监听器的类型
Type getType() const { return _type; }
// 返回监听器的ID
const ListenerID& getListenerID() const { return _listenerID; }
// 为监听器设置fixed priority
void setFixedPriority(int fixedPriority) { _fixedPriority = fixedPriority; }
// 返回该监听器的fixed priority
int getFixedPriority() const { return _fixedPriority; }
// 设置该监听器关联的结点
void setAssociatedNode(Node* node) { _node = node; }
// 返回该监听器关联的结点
Node* getAssociatedNode() const { return _node; }
// 属性
// 事件回调函数
std::function<void(Event*)> _onEvent;
// 监听器的类型
Type _type;
// 监听器的ID
ListenerID _listenerID;
// 该监听器是否已经再EventDispatcher中注册
bool _isRegistered;
// 该数字越高,表示该优先级越大
int _fixedPriority;
// 基于场景图的优先级
Node* _node;
bool _paused;
bool _isEnabled;
friend class EventDispatcher;
};
fixedPriority 表示事件的优先级,_node 表示事件依赖的场景结点,这两个属性是对立的,分别对应两种添加事件监听器的方式,一种是事件监听器依赖于场景结点,则需要保存这个结点(_node),这时事件不需要额外定义优先级;另一种是事件监听器注册在全局中,则需要定义事件的优先级(_fixedPriority)
针对不同的Event,自然就有不同的EventListener,不同的监听器通过继承EventListener基类来实现不同的功能,其类型也是由enum Type所规定了。监听器的类型比事件类 多了两个,一个是UNKNOWN 另一个是将TOUCH 细分为两种,总的来说还是一一对应的。
其中,init函数在子类初始化的时候会调用,而checkAvaliable方法则在注册事件监听器的时候调用,检查回调函数是否为空,如果为空,则不能注册监听器。
下面还是以键盘事件的监听器为例子进行分析:
class CC_DLL EventListenerKeyboard : public EventListener
{
public:
static const std::string LISTENER_ID;
/** Create a keyboard event listener.
*
* @return An autoreleased EventListenerKeyboard object.
*/
static EventListenerKeyboard* create();
/// Overrides
virtual EventListenerKeyboard* clone() override;
virtual bool checkAvailable() override;
std::function<void(EventKeyboard::KeyCode, Event*)> onKeyPressed;
std::function<void(EventKeyboard::KeyCode, Event*)> onKeyReleased;
CC_CONSTRUCTOR_ACCESS:
EventListenerKeyboard();
bool init();
};
该类新定义了两个回调函数 onKeyPressed 和 onKeyReleased。这两个子回调函数将由父类定义的回调函数 _onEvent 调用。
bool EventListenerKeyboard::init()
{
auto listener = [this](Event* event){
auto keyboardEvent = static_cast<EventKeyboard*>(event);
if (keyboardEvent->_isPressed)
{
if (onKeyPressed != nullptr)
onKeyPressed(keyboardEvent->_keyCode, event);
}
else
{
if (onKeyReleased != nullptr)
onKeyReleased(keyboardEvent->_keyCode, event);
}
};
if (EventListener::init(Type::KEYBOARD, LISTENER_ID, listener))
{
return true;
}
return false;
}
该子类的init函数首先会定义新的两个回调函数 onKeyPressed 和 onKeyReleased,然后再调用父类的init函数。
而重载后的checkAvailable()
则是检查这两个子回调函数是否为空
EventDispatcher 的作用主要有两方面:
EventDispatcher 定义了一个私有类 EventListenerVector 来存储注册的事件监听器,所有我们先看这个EventListenerVector私有类
// 存储两个SceneGraph与Fixed优先级的监听器
class EventListenerVector
{
public:
EventListenerVector();
~EventListenerVector();
size_t size() const;
bool empty() const;
void push_back(EventListener* item);
void clearSceneGraphListeners();
void clearFixedListeners();
void clear();
std::vector<EventListener*>* getFixedPriorityListeners() const { return _fixedListeners; }
std::vector<EventListener*>* getSceneGraphPriorityListeners() const { return _sceneGraphListeners; }
ssize_t getGt0Index() const { return _gt0Index; }
void setGt0Index(ssize_t index) { _gt0Index = index; }
private:
std::vector<EventListener*>* _fixedListeners;
std::vector<EventListener*>* _sceneGraphListeners;
ssize_t _gt0Index;
};
EventListenerVector 用两个向量Vector来保存监听器,分别对应两种监听器.
// SceneGraph优先级的事件监听器
void addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node);
// Fixed优先级的事件监听器
void addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority);
// 客户自定义的事件监听器
EventListenerCustom* addCustomEventListener(const std::string &eventName, const
std::function<void(EventCustom*)>& callback);
以其中一个为例
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);
listener->setRegistered(true);
addEventListener(listener);
}
首先检查监听器(和结点)是否为空,检查监听器是否已注册(不能重复注册),检查监听器是否合法,即对应的回调函数是否有定义;
然后设置依赖的结点、优先级,设置为已注册,最后调用私有函数addEventListener
注册事件监听器
void EventDispatcher::addEventListener(EventListener* listener)
{
if (_inDispatch == 0)
{
forceAddEventListener(listener);
}
else
{
_toAddedListeners.push_back(listener);
}
#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
if (sEngine)
{
sEngine->retainScriptObject(this, listener);
}
#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
listener->retain();
}
addEventListener 先判断事件分发器当前是否在派发事件,如果是的话则先将事件监听器放到一个队列中去,否则则调用 forceAddEventListener
注册事件监听器
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();
//将 EventListenerVector 保存在一个字典
_listenerMap.emplace(listenerID, listeners);
}
else
{
listeners = itr->second;
}
listeners->push_back(listener);
if (listener->getFixedPriority() == 0)
{
// 设置dirty flag
setDirty(listenerID, DirtyFlag::SCENE_GRAPH_PRIORITY);
auto node = listener->getAssociatedNode();
CCASSERT(node != nullptr, "Invalid scene graph priority!");
// 关联节点
associateNodeAndEventListener(node, listener);
if (!node->isRunning())
{
listener->setPaused(true);
}
}
else
{
setDirty(listenerID, DirtyFlag::FIXED_PRIORITY);
}
}
_listenerMap 是监听器ID-EventListenerVector 键值对,也就是同一类型(ID)的监听器存储在一个 EventListenerVector 中,而这些监听器可以注册在结点上,也可能独立注册,EventListenerVector 会将它们放在不同的向量列表中,看一下它的 push_back
方法
void EventDispatcher::EventListenerVector::push_back(EventListener* listener)
{
#if CC_NODE_DEBUG_VERIFY_EVENT_LISTENERS
CCASSERT(_sceneGraphListeners == nullptr ||
std::count(_sceneGraphListeners->begin(), _sceneGraphListeners->end(), listener) == 0,
"Listener should not be added twice!");
CCASSERT(_fixedListeners == nullptr ||
std::count(_fixedListeners->begin(), _fixedListeners->end(), listener) == 0,
"Listener should not be added twice!");
#endif
// secneGraph优先级的监听器
if (listener->getFixedPriority() == 0)
{
if (_sceneGraphListeners == nullptr)
{
_sceneGraphListeners = new (std::nothrow) std::vector<EventListener*>();
_sceneGraphListeners->reserve(100);
}
_sceneGraphListeners->push_back(listener);
}
// fixed优先级的监听器
else
{
if (_fixedListeners == nullptr)
{
_fixedListeners = new std::vector<EventListener*>();
_fixedListeners->reserve(100);
}
_fixedListeners->push_back(listener);
}
}
上面说完了forceAddEventListener
部分,那么另一部分加入到队列_toAddedListeners中,后面会接着什么操作呢。实际上,为了保证逻辑的正确性,EventDispatcher 在派发事件过程中是不能注册事件监听器,故将这些事件暂时放在了_toAddedListeners中,之后等到分发器空闲的时候会调用updateListeners
来注册这些暂时被挂起的监听器。
void EventDispatcher::updateListeners(Event* event)
{
// updateListeners 会根据事件类型更新这一事件监听器 ID 对应的所有监听器
······
// 将之前挂起的监听器重新注册
if (!_toAddedListeners.empty())
{
for (auto& listener : _toAddedListeners)
{
forceAddEventListener(listener);
}
_toAddedListeners.clear();
}
if (!_toRemovedListeners.empty())
{
cleanToRemovedListeners();
}
}
遍历 _toAddedListeners 列表,调用 forceAddEventListener 注册这些挂起的监听器;
而后面的_toRemovedListeners与添加监听器一样,临时挂起不能及时移除的监听器,到update的时候才遍历进行移除。
void removeEventListener(EventListener* listener);
void removeEventListenersForListenerID(const EventListener::ListenerID& listenerID);
void removeEventListenersForType(EventListener::Type listenerType);
void removeEventListenersForTarget(Node* target, bool recursive = false);
void removeCustomEventListeners(const std::string& customEventName);
void removeAllEventListeners();
其中最为关键的函数是removeEventListener
void EventDispatcher::removeEventListener(EventListener* listener)
{
if (listener == nullptr)
return;
// just return if listener is in _toRemovedListeners to avoid remove listeners more than once
if (std::find(_toRemovedListeners.begin(), _toRemovedListeners.end(), listener) != _toRemovedListeners.end())
return;
bool isFound = false;
// 遍历 _listenerMap 得到一个监听器 ID 对应的监听器列表 listeners
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)
{
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)
{
iter = listeners->erase(iter);
releaseListener(l);
}
else
{
_toRemovedListeners.push_back(l);
}
isFound = true;
break;
}
}
};
for (auto iter = _listenerMap.begin(); iter != _listenerMap.end();)
{
// 分别遍历 listeners 的两个子列表 fixedPriorityListeners
// sceneGraphPriorityListeners 里的监听器
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 (iter->second->empty())
{
_priorityDirtyFlagMap.erase(listener->getListenerID());
auto list = iter->second;
iter = _listenerMap.erase(iter);
CC_SAFE_DELETE(list);
}
else
{
++iter;
}
if (isFound)
break;
}
// step3 将监听器从列表移除后,该列表可能为空了,即没有该监听器 ID 对应的监听器了,所以要把这个列表从 _listenerMap 移除
if (isFound)
{
releaseListener(listener);
}
else
{
for(auto iter = _toAddedListeners.begin(); iter != _toAddedListeners.end(); ++iter)
{
if (*iter == listener)
{
listener->setRegistered(false);
releaseListener(listener);
_toAddedListeners.erase(iter);
break;
}
}
}
}
void EventDispatcher::dispatchEvent(Event* event)
{
if (!_isEnabled)
return;
updateDirtyFlagForSceneGraph();
// _inDispatch用于表示当前正有多少事件正在分发。
DispatchGuard guard(_inDispatch);
// TOUCH事件要另外处理,因为有两种情况
if (event->getType() == Event::Type::TOUCH)
{
dispatchTouchEvent(static_cast<EventTouch*>(event));
return;
}
// 根据事件类型得到事件监听器类型
auto listenerID = __getListenerID(event);
// 排序 listener,找出优先级最高的
sortEventListeners(listenerID);
auto pfnDispatchEventToListeners = &EventDispatcher::dispatchEventToListeners;
// 鼠标事件也看作TOUCH来处理
if (event->getType() == Event::Type::MOUSE) {
pfnDispatchEventToListeners = &EventDispatcher::dispatchTouchEventToListeners;
}
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();
};
(this->*pfnDispatchEventToListeners)(listeners, onEvent);
}
updateListeners(event);
}
在监听器的新建与删除中有很多涉及到了Dirty Flag,这个脏的标记的作用是监听器列表需要重新排序。代码中可以看到,这个 DirtyFlag 是设置给 ListenerID
的,每当新添加一个 Listener
,或则删除一个 Listener
的时候,就会给当前 Listener
的 ListenerID
添加一个 DirtyFlag。说明这个脏是指 ListenerID
对应的监听器向量列表需要重新排序了,如果不脏就不需要排序。