cocos2dx之event事件(三):事件分发器EventDispatcher

 

@class EventDispatcher

 

 @brief This class manages event listener subscriptions and event dispatching.

首先上一段EventDispatcher的官方注释,解释了EventDispatcher的作用是管理listener和做事件分发.

进一步理解,EventDispatcher主要负责存储Listener以及当发生event时,把event发给监听此类event的listener并且多个listener监听相同event的时候,dispatcher来决定先后顺序.EventDispatcher的数据成员和其作用如下:

 

class CC_DLL EventDispatcher : public Ref
{
	//函数已省略
protected:
	//存储listeners的map容器
	std::unordered_map<EventListener::ListenerID, EventListenerVector*> _listenerMap;
	//脏标志map容器,用于判断Listener是否需要重新排序
	std::unordered_map<EventListener::ListenerID, DirtyFlag> _priorityDirtyFlagMap;
	//存储场景层监听器与它们的node
	std::unordered_map<Node*, std::vector<EventListener*>*> _nodeListenersMap;
	/** The map of node and its event priority */
	std::unordered_map<Node*, int> _nodePriorityMap;
	/** key: Global Z Order, value: Sorted Nodes */
	std::unordered_map<float, std::vector<Node*>> _globalZOrderNodeMap;
	/** The listeners to be added after dispatching event */
	std::vector<EventListener*> _toAddedListeners;
	/** The listeners to be removed after dispatching event */
	std::vector<EventListener*> _toRemovedListeners;
	/** The nodes were associated with scene graph based priority listeners */
	std::set<Node*> _dirtyNodes;
	//分发器否正在分发事件的数量:0没有事件分发,大于0有事件分发
	int _inDispatch;
	//分发器是否工作
	bool _isEnabled;
	int _nodePriorityIndex;
	std::set<std::string> _internalCustomListenerIDs;
};

 

通过EventDispatcher的头文件可知,其成员变量主要以一些容器为主,其中_listenerMap是最重要的map,用来存储各种EventListener。再来看

 

EventListenerVector,它是EventDispatcher内部的一个类:

 

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;
};

 

受EventDispatcher管理的Listener根据其优先级是否为0,被存储到_fixedListeners和_sceneGraphListeners两个向量容器内.

 

接下来通过跟踪源码看一下当在EventDispatcher中加入一个EventListener后,发生了什么.

 

 

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);//
}
void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)
{
	CCASSERT(listener, "Invalid parameters.");
	CCASSERT(!listener->isRegistered(), "The listener has been registered.");//检测一个监听器只能注册一次
	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);
}
void EventDispatcher::addEventListener(EventListener* listener)
{
	if (_inDispatch == 0)//如果EventDispatcher处于空闲状态
	{
		forceAddEventListener(listener);//执行常规添加操作
	}
	else                 //如果EventDispatcher正在分发事件
	{
		_toAddedListeners.push_back(listener);//将listener放入向量容器中
	}
	listener->retain();
}
void EventDispatcher::forceAddEventListener(EventListener* listener)
{
	//1.添加listener
	EventListenerVector* listeners = nullptr;
	EventListener::ListenerID listenerID = listener->getListenerID();//一类listener的ID都一样
	auto itr = _listenerMap.find(listenerID);//查找该类listener
	if (itr == _listenerMap.end())//如果没找到,则在map中添加新的pair
	{
		listeners = new (std::nothrow) EventListenerVector();
		_listenerMap.insert(std::make_pair(listenerID, listeners));
	}
	else//找到该类listener就获取保存listener的EventListenerVector
	{
		listeners = itr->second;
	}
	listeners->push_back(listener);
	//2.设置脏标志(用于排序)
	if (listener->getFixedPriority() == 0)
	{
		setDirty(listenerID, DirtyFlag::SCENE_GRAPH_PRIORITY);
		auto node = listener->getAssociatedNode();
		CCASSERT(node != nullptr, "Invalid scene graph priority!");
		//3.将场景层listener和node关联起来
		associateNodeAndEventListener(node, listener);//维护_nodeListenersMap容器
		if (node->isRunning())
		{
			resumeEventListenersForTarget(node);
		}
	}
	else
	{
		setDirty(listenerID, DirtyFlag::FIXED_PRIORITY);
	}
}

 

最后看下核心函数dispatchEvent,该函数每帧都会执行

 

void EventDispatcher::dispatchEvent(Event* event)
{
	if (!_isEnabled)
		return;
	updateDirtyFlagForSceneGraph();
	DispatchGuard guard(_inDispatch);//更改_inDispatch状态,执行后变为1
	if (event->getType() == Event::Type::TOUCH)//触摸事件单独处理
	{
		dispatchTouchEvent(static_cast<EventTouch*>(event));
		return;
	}
	auto listenerID = __getListenerID(event);//通过event类型获取listenerID
	sortEventListeners(listenerID);//对EventListener排序
	auto pfnDispatchEventToListeners = &EventDispatcher::dispatchEventToListeners;//获取分发监听器函数的指针
	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{//每个响应事件的listener执行的lambda
			event->setCurrentTarget(listener->getAssociatedNode());
			listener->_onEvent(event);//监听器执行自身的响应函数
			return event->isStopped();
		};
		(this->*pfnDispatchEventToListeners)(listeners, onEvent);//执行事件分发
	}
	updateListeners(event);//更新Listener
}

 

void EventDispatcher::dispatchTouchEvent(EventTouch* event)//分发触摸事件
{
	sortEventListeners(EventListenerTouchOneByOne::LISTENER_ID);//单点触摸监听器排序
	sortEventListeners(EventListenerTouchAllAtOnce::LISTENER_ID);//多点触摸监听器排序
	auto oneByOneListeners = getListeners(EventListenerTouchOneByOne::LISTENER_ID);
	auto allAtOnceListeners = getListeners(EventListenerTouchAllAtOnce::LISTENER_ID);
	//如果没有touch listeners, 直接返回
	if (nullptr == oneByOneListeners && nullptr == allAtOnceListeners)
		return;
	bool isNeedsMutableSet = (oneByOneListeners && allAtOnceListeners);
	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();
		for (; touchesIter != originalTouches.end(); ++touchesIter)// 单点触摸只有一个Touch坐标
		{
			bool isSwallowed = false;
			auto onTouchEvent = [&](EventListener* l) -> bool { // 被分发事件的监听器执行的lambda
				EventListenerTouchOneByOne* listener = static_cast<EventListenerTouchOneByOne*>(l);
				// Skip if the listener was removed.
				if (!listener->_isRegistered)
					return false;
				event->setCurrentTarget(listener->_node);
				bool isClaimed = false;
				std::vector<Touch*>::iterator removedIter;
				EventTouch::EventCode eventCode = event->getEventCode();//eventCode用来对触摸事件进行细分
																		//根据eventCode为BEGAN,MOVED,ENDED,CANCELLED 4种情况,调用不同的callback
				if (eventCode == EventTouch::EventCode::BEGAN)
				{
					if (listener->onTouchBegan)
					{
						isClaimed = listener->onTouchBegan(*touchesIter, event);
						if (isClaimed && listener->_isRegistered)
						{
							listener->_claimedTouches.push_back(*touchesIter);
						}
					}
				}
				else if (listener->_claimedTouches.size() > 0
					&& ((removedIter = std::find(listener->_claimedTouches.begin(), listener->_claimedTouches.end(), *touchesIter)) != listener->_claimedTouches.end()))
				{
					isClaimed = true;
					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(),
					"touchesIter ID should be equal to mutableTouchesIter's ID.");
				if (isClaimed && listener->_isRegistered && listener->_needSwallow)
				{
					if (isNeedsMutableSet)
					{
						mutableTouchesIter = mutableTouches.erase(mutableTouchesIter);
						isSwallowed = true;
					}
					return true;
				}
				return false;
			};//end of lambda
			dispatchTouchEventToListeners(oneByOneListeners, onTouchEvent);//单点触摸事件分给监听器
			if (event->isStopped())
			{
				return;
			}
			if (!isSwallowed)
				++mutableTouchesIter;
		}//end of for
	}//end of if
	//多点触摸
	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;
		};
		dispatchTouchEventToListeners(allAtOnceListeners, onTouchesEvent);
		if (event->isStopped())
		{
			return;
		}
	}
	updateListeners(event);

}

实际分发触摸和鼠标事件的函数是dispatchTouchEventToListeners,其他类型事件是dispatchEventToListeners,两者实现很像,这里只看前者源码.

void EventDispatcher::dispatchTouchEventToListeners(EventListenerVector* listeners, const std::function<bool(EventListener*)>& onEvent)
{
	bool shouldStopPropagation = false;
	auto fixedPriorityListeners = listeners->getFixedPriorityListeners();//获得fixPriority的vector
	auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners();//获得sceneGraphPriority的vector
	ssize_t i = 0;
	if (fixedPriorityListeners)
	{
		CCASSERT(listeners->getGt0Index() <= static_cast<ssize_t>(fixedPriorityListeners->size()), "Out of range exception!");
		if (!fixedPriorityListeners->empty())
		{
			for (; i < listeners->getGt0Index(); ++i)//只遍历优先级小于0的监听器
			{
				auto l = fixedPriorityListeners->at(i);//遍历fixedPriorityListeners[0]-fixedPriorityListeners[listeners->getGt0Index()-1],listeners->getGt0Index()在排序中维护
				if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))//执行事件回调函数
				{
					shouldStopPropagation = true;
					break;
				}
			}
		}
	}
	auto scene = Director::getInstance()->getRunningScene();
	if (scene && sceneGraphPriorityListeners)
	{
		if (!shouldStopPropagation)
		{
			// priority == 0, scene graph priority
			// first, get all enabled, unPaused and registered listeners
			std::vector<EventListener*> sceneListeners;
			for (auto& l : *sceneGraphPriorityListeners)
			{
				if (l->isEnabled() && !l->isPaused() && l->isRegistered())
				{
					sceneListeners.push_back(l);
				}
			}
			// second, for all camera call all listeners
			// get a copy of cameras, prevent it's been modified in listener callback
			// if camera's depth is greater, process it earlier
			auto cameras = scene->getCameras();
			Camera* camera;
			for (int j = int(cameras.size()) - 1; j >= 0; --j)
			{
				camera = cameras[j];
				if (camera->isVisible() == false)
				{
					continue;
				}
				Camera::_visitingCamera = camera;
				auto cameraFlag = (unsigned short)camera->getCameraFlag();
				for (auto& l : sceneListeners)
				{
					if (nullptr == l->getAssociatedNode() || 0 == (l->getAssociatedNode()->getCameraMask() & cameraFlag))
					{
						continue;
					}
					if (onEvent(l))
					{
						shouldStopPropagation = true;
						break;
					}
				}
				if (shouldStopPropagation)
				{
					break;
				}
			}
			Camera::_visitingCamera = nullptr;
		}
	}
	if (fixedPriorityListeners)
	{
		if (!shouldStopPropagation)
		{
			ssize_t size = fixedPriorityListeners->size();
			for (; i < size; ++i)//只遍历优先级大于0的监听器
			{
				auto l = fixedPriorityListeners->at(i);//遍历fixedPriorityListeners[listeners->getGt0Index()]-fixedPriorityListeners->size()-1,listeners->getGt0Index()在排序中维护

				if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
				{
					shouldStopPropagation = true;
					break;
				}
			}
		}
	}
}

最后考虑下如果在监听响应函数中,再添加监听器并设置响应函数,那么是怎么处理?

这种情况新加的监听器会在下次触发事件时起作用,再来看下addEventListener和updateListeners(event)两个函数:

 

void EventDispatcher::addEventListener(EventListener* listener)
{
	if (_inDispatch == 0)
	{
		forceAddEventListener(listener);//EventDispatcher空闲,常规添加listener
	}
	else
	{
		_toAddedListeners.push_back(listener);//EventDispatcher繁忙,将在updateListeners(event)中添加listener
	}

	listener->retain();
}
void EventDispatcher::updateListeners(Event* event)
{
	CCASSERT(_inDispatch > 0, "If program goes here, there should be event in dispatch.");
	if (_inDispatch > 1)
		return;
	//中间省略
	if (!_toAddedListeners.empty())
	{
		for (auto& listener : _toAddedListeners)
		{
			forceAddEventListener(listener);//常规添加listener
		}
		_toAddedListeners.clear();
	}

	if (!_toRemovedListeners.empty())
	{
		cleanToRemovedListeners();//常规删除listener
	}
}

 

dispatchEvent总结:

1.调用sortEventListeners对listener排序
2.调用dispatchTouchEventToListeners并传listenervVector和回调onTouchEvent给它

3.按照监听器优先级小于0,优先级==0,优先级>0的顺序执行回调函数

4.调用updateListeners(event);

另外有几个注意事项:

1.当TouchEvent Began来了之后,所有的listener会依次响应Touch Began,然后再依次响应Touch Move,而不是一个listener响应完  began move end之后 轮到下一个listener响应的顺序。

2.吞噬操作只有发生在began return true后才可以发生

3.FixedPriority listener添加完之后需要手动remove,而SceneGraphPriority listener是跟Node绑定的,在Node的析构函数中会被移除。


面试环节

问:介绍下cocos2d-x事件机制

答:和事件相关最重要的3个类为Event,EventListener和EventDispatcher。

Event类定义了事件的种类,如触摸,鼠标,键盘,加速度等。

EventListener最主要的任务是保存事件的回调函数,EventListener中有个优先级变量,根据该值是否为0将EventListener分为两类,优先级为0的为场景监听器,非0为固定监听器。另外优先级会被用来判断监听器中回调函数执行先后顺序,优先级越低的监听器越先接到事件。

第三个类EventDispatcher的作用是管理listener和做事件分发。每一帧的drawScene中会调用pollEvent来检测事件,pollEvent中执行dispatchEvent;dispatchEvent首先会对listener排序,然后按照监听器优先级小于0,等于0,大于0的顺序执行回调函数,最后更新监听器。
 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ellis1970

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值