本文介绍Cocos2dx事件(以下简称Event)处理机制中的事件分发模块,在Event发生后,进过一系列处理,最后将会分发Event;

1、dispatchEvent& dispatchTouchEvent方法

voidEventDispatcher::dispatchEvent(Event* event)
{
if (!_isEnabled) return;
updateDirtyFlagForSceneGraph();
DispatchGuard guard(_inDispatch);
if (event->getType() ==Event::Type::TOUCH) {
dispatchTouchEvent(static_cast<EventTouch*>(event));
return;
}
auto listenerID = __getListenerID(event);
sortEventListeners(listenerID);
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();
};
dispatchEventToListeners(listeners,onEvent);
}
updateListeners(event);
}

在dispatchEvent方法中:

(1)  推断分发Event机制是否使能

(2)  更新脏数据标志

(3)  分发触摸Event

(4)  分发其它类型Event

voidEventDispatcher::dispatchTouchEvent(EventTouch* event)
{
sortEventListeners(EventListenerTouchOneByOne::LISTENER_ID);
sortEventListeners(EventListenerTouchAllAtOnce::LISTENER_ID);
auto oneByOneListeners =getListeners(EventListenerTouchOneByOne::LISTENER_ID);
auto allAtOnceListeners =getListeners(EventListenerTouchAllAtOnce::LISTENER_ID);
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());
if (oneByOneListeners)
{
auto mutableTouchesIter =mutableTouches.begin();
auto touchesIter =originalTouches.begin();
for (; touchesIter !=originalTouches.end(); ++touchesIter) {
bool isSwallowed = false;
auto onTouchEvent =[&](EventListener* l) -> bool { ....
};
dispatchEventToListeners(oneByOneListeners, onTouchEvent);
if (event->isStopped()){
return;
}
if (!isSwallowed)
++mutableTouchesIter;
}
}
if (allAtOnceListeners &&mutableTouches.size() > 0) {
auto onTouchesEvent =[&](EventListener* l) -> bool{
....
};
dispatchEventToListeners(allAtOnceListeners, onTouchesEvent);
if (event->isStopped()){
return;
}
}
updateListeners(event);
}

在dispatchTouchEvent方法中:

(1) 对单指点击&多指点击EventListener列表进行排序;当然排序算法中首先推断眼下EventListener列表是否为脏数据,假设是脏数据,则进行排序;排序的具体细节以下会具体讲述

(2) 获取单指点击&多指点击EventListener列表,并推断EventListener是否为空

(3) 获取Event信息

(4) 若单指点击EventListener列表不为空,则分发单指点击Event

(5) 若多指点击EventListener列表不为空,则分发多指点击Event

(6) 更新EventListener列表状态

2、dispatchTouchEvent 方法中EvnetListener排序sortEventListeners

在dispatchTouchEvent方法中使用了sortEventListeners方法对EventListener列表进行排序,以下将具体解说该方法;

voidEventDispatcher::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) {
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;
}
}
}
}

在sortEventListeners方法中:

(1)  在脏数据列表中查找该listenerID的EventListener是否存在脏数据,若不存在脏数据则不须要排序,退出该方法;若存在脏数据,则进行排序

(2)  针对优先级不等于0的EventListener列表进行排序

(3)  针对优先级等于0的EventListener列表进行排序

以下为优先级不等于0的EventListener列表排序方法:

voidEventDispatcher::sortEventListenersOfFixedPriority(constEventListener::ListenerID& listenerID) {
auto listeners = getListeners(listenerID);
if (listeners == nullptr) return;
auto fixedListeners =listeners->getFixedPriorityListeners();
if (fixedListeners == nullptr) return;
std::sort(fixedListeners->begin(),fixedListeners->end(), [](const EventListener* l1, const EventListener* l2){
return l1->getFixedPriority() <l2->getFixedPriority();
});
intindex = 0;
for (auto& listener : *fixedListeners){
if (listener->getFixedPriority()>= 0)
break;
++index;
}
listeners->setGt0Index(index);
}

在sortEventListenersOfFixedPriority方法中:

(1) 依据ID获取EventListener列表,并推断列表是否为空

(2) 获取EventListener列表中优先级不等于0的EventListener列表_fixedListeners

(3) 使用STL中sort方法对_fixedListeners方法从小到大排序

(4) 统计fixedListeners类表中优先级数值小于0的EventListener的个数

从排序的方法能够得知,高优先级(数值越小优先级越高)EventListener先运行;若优先级同样,先注冊的EventListener先运行

以下为优先级等于0的EventListener列表排序方法:

voidEventDispatcher::sortEventListenersOfSceneGraphPriority(constEventListener::ListenerID& listenerID, Node* rootNode) {
auto listeners = getListeners(listenerID);
if (listeners == nullptr) return;
auto sceneGraphListeners =listeners->getSceneGraphPriorityListeners();
if (sceneGraphListeners == nullptr) return;
_nodePriorityIndex = 0;
_nodePriorityMap.clear();
visitTarget(rootNode, true);
std::sort(sceneGraphListeners->begin(),sceneGraphListeners->end(), [this](const EventListener* l1, constEventListener* l2) {
return_nodePriorityMap[l1->getAssociatedNode()] > _nodePriorityMap[l2->getAssociatedNode()];
});
}

在sortEventListenersOfSceneGraphPriority方法中:

(1) 依据ID获取EventListener列表,并推断列表是否为空

(2) 获取EventListener列表中优先级等于0的EventListener列表_sceneGraphListeners

(3) 使用_globalZOrder值对该Scene下的Node排序

(4) 依据EventListener相应Node的_globalZOrder值从大到小将_sceneGraphListeners列表排序

3、dispatchTouchEvent 方法中dispatchEventToListeners方法

voidEventDispatcher::dispatchEventToListeners(EventListenerVector* listeners, conststd::function<bool(EventListener*)>& onEvent) {
bool shouldStopPropagation = false;
auto fixedPriorityListeners =listeners->getFixedPriorityListeners();
auto sceneGraphPriorityListeners =listeners->getSceneGraphPriorityListeners();
ssize_t i = 0;
if (fixedPriorityListeners) {
if(!fixedPriorityListeners->empty()){
for (; i <listeners->getGt0Index(); ++i) {
auto l =fixedPriorityListeners->at(i);
if (l->isEnabled()&& !l->isPaused() && l->isRegistered() &&onEvent(l)) {
shouldStopPropagation =true;
break;
}
}
}
}
if (sceneGraphPriorityListeners) {
if (!shouldStopPropagation) {
for (auto& l :*sceneGraphPriorityListeners) {
if (l->isEnabled()&& !l->isPaused() && l->isRegistered() &&onEvent(l)) {
shouldStopPropagation =true;
break;
}
}
}
}
if (fixedPriorityListeners) {
if (!shouldStopPropagation) {
ssize_t size =fixedPriorityListeners->size();
for (; i < size; ++i) {
auto l =fixedPriorityListeners->at(i);
if (l->isEnabled()&& !l->isPaused() && l->isRegistered() &&onEvent(l)) {
shouldStopPropagation = true;
break;
}
}
}
}
}

在dispatchEventToListeners函数中:

(1) 获取_fixedListeners&_sceneGraphListeners列表

(2) 当_fixedListeners不为空时;运行_fixedListeners类表中EventToListener处理

(3) 当_sceneGraphListeners不为空时;运行_sceneGraphListeners类表中EventToListener处理

在_fixedListeners类表中EventToListener处理中,优先级小于0的是不运行的处理方法的;EventToListener的处理方法是通过參数传递过来的匿名函数;该匿名函数的实现以下会继续讲述

4、dispatchTouchEvent 方法中单点匿名方法onTouchEvent

auto onTouchEvent =[&](EventListener* l) -> bool {
EventListenerTouchOneByOne* listener =static_cast<EventListenerTouchOneByOne*>(l);
if (!listener->_isRegistered) returnfalse;
event->setCurrentTarget(listener->_node);
bool isClaimed = false;
std::vector<Touch*>::iteratorremovedIter;
EventTouch::EventCode eventCode =event->getEventCode();
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;
caseEventTouch::EventCode::CANCELLED:
if (listener->onTouchCancelled){
listener->onTouchCancelled(*touchesIter,event);
}
if (listener->_isRegistered) {
listener->_claimedTouches.erase(removedIter);
}
break;
default:
CCASSERT(false, "Theeventcode is invalid.");
break;
}
}
if (event->isStopped()){
updateListeners(event);
return true;
}
if (isClaimed &&listener->_isRegistered && listener->_needSwallow) {
if (isNeedsMutableSet) {
mutableTouchesIter =mutableTouches.erase(mutableTouchesIter);
isSwallowed = true;
}
return true;
}
return false;
};

在匿名方法onTouchEvent中:

(1) 将传递參数强制转换成EventListenerTouchOneByOne类型,并推断是否为空

(2) 获取触摸(Win32下为鼠标点击\拖动)Event类型

(3) 推断Event类型是BEGAN时

a)  调用EventListener在注冊时指定的onTouchBegan,并获取返回值

b)  若返回值是true,将该Event的Touch信息放入_claimedTouches中

(4) 推断Event类型不是BEGAN时

a)  _claimedTouches的内容不为空,在_claimedTouches中有该Event的Touch信息

b)  若Event类型是MOVED,调用EventListener在注冊时指定的onTouchMoved

c)  若Event类型是ENDED,调用EventListener在注冊时指定的onTouchEnded

d)  若Event类型是CANCELLED,调用EventListener在注冊时指定的onTouchCancelled

e)  将该Event的Touch信息从_claimedTouches中移除

(5) 若该Event被停止,更新EventListener列表

(6) 若在onTouchBegan返回值为true,而且_needSwallow被设置为true时,将当前Event从多点触摸Event列表中移除

在该匿名方法中,onTouchBegan的返回值非常重要,他关注着兴许其它触摸操作(onTouchMoved\onTouchEnded\onTouchCancelled)是否运行,关注则_needSwallow标志是否生效;

5、dispatchTouchEvent 方法中多点的匿名方法onTouchEvent

auto onTouchesEvent= [&](EventListener* l) -> bool{
EventListenerTouchAllAtOnce* listener =static_cast<EventListenerTouchAllAtOnce*>(l);
if (!listener->_isRegistered) returnfalse;
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 eventcodeis invalid.");
break;
}
if (event->isStopped()){
updateListeners(event);
return true;
} return false;
};

在匿名方法onTouchEvent中:

(1)  将传递參数强制转换成EventListenerTouchAllAtOnce类型,并推断是否为空

(2)  获取触摸(Win32下为鼠标点击\拖动)Event类型

(3)  若Event类型为BEGAN时,调用EventListener在注冊时指定的onTouchBegan方法

(4)  若Event类型为MOVED时,调用EventListener在注冊时指定的onTouchesMoved方法

(5)  若Event类型为ENDED时,调用EventListener在注冊时指定的onTouchesEnded方法

(6)  若Event类型为CANCELLED时,调用EventListener在注冊时指定的onTouchesCancelled方法

(7)  若该Event被停止,更新EventListener列表

Cocos2dx引擎10-事件派发的更多相关文章

  1. cocos2d-x 3.10 PageView BUG

    cocos2d-x 3.10 PageView 拖动滚动到下一个单元,没事件,3.11有修复.

  2. cocos2dx+lua注册事件函数详解

    coocs2dx 版本 3.1.1 registerScriptTouchHandler 注册触屏事件 registerScriptTapHandler 注册点击事件 registerScriptHa ...

  3. cocos2dx+lua注册事件函数详解 事件

    coocs2dx 版本 3.1.1 registerScriptTouchHandler             注册触屏事件 registerScriptTapHandler             ...

  4. 48、[源码]-Spring容器创建-初始化事件派发器、监听器等

    48.[源码]-Spring容器创建-初始化事件派发器.监听器等 8.initApplicationEventMulticaster();初始化事件派发器: 获取BeanFactory 从BeanFa ...

  5. Java多线程开发系列之番外篇:事件派发线程---EventDispatchThread

    事件派发线程是java Swing开发中重要的知识点,在安卓app开发中,也是非常重要的一点.今天我们在多线程开发中,穿插进来这个线程.分别从线程的来由.原理和使用方法三个方面来学习事件派发线程. 一 ...

  6. wex5 实战 框架拓展之2 事件派发与data刷新

    一 前言 讲完公共data,相信大家对框架级的data组件级绑定有了更新的认识,接下来我们继续深入,以求研究明白wex5的框架能力. 在一个web项目中,其实有一个data, 是基础框架必须的data ...

  7. 使用lua实现一个简单的事件派发器

    设计一个简单的事件派发器,个人觉得最重要的一点就是如何保证事件派发过程中,添加或删除同类事件,不影响事件迭代顺序和结果,只要解决这一点,其它都好办. 为了使用pairs遍历函数,重写了pairs(lu ...

  8. cocos2d-x lua 触摸事件

    cocos2d-x lua 触摸事件 version: cocos2d-x 3.6 1.监听 function GameLayer:onEnter() local eventDispatcher = ...

  9. [置顶] Android源码分析-点击事件派发机制

    转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/17339857 概述 一直想写篇关于Android事件派发机制的文章,却一直没 ...

随机推荐

  1. php - 微信 - 缓存access_token类。

    可扩展性很强. <?php namespace LaneWeChat\Core; /** * 微信Access_Token的获取与过期检查 * Created by Lane. * User: ...

  2. java的数据类型,几个java小程序

    1:求圆的面积 还好看了c++,不然直接看这课件还真是看不懂……加油吧 要从键盘读入数据可以用Scanner类的nextlnt()或者nextDouble()方法,首先创建Scanner类的一个实例, ...

  3. 比赛--找丢失的数--解题报告T

    找丢失的数 题目大意: There is a permutation without two numbers in it, and now you know what numbers the perm ...

  4. BZOJ 1458: 士兵占领( 网络流 )

    先判无解 把整个棋盘都放上士兵, 只需求最多可以拿走多少个士兵即可.每一行看做一个点r(i), 每一列看做一个点c(i) S->r(i), c(i)->T 连边, 容量为可以拿走的最大士兵 ...

  5. C# 继承细节

    假定没有为类定义任何显式的构造函数,这样编译器就会为所有的类提供默认的构造函数,在后台会进行许多操作,编译器可以很好地解决层次结构中的所有问题,每个类中的每个字段都会初始化为默认值.但在添加了一个我们 ...

  6. HDU2138 随机素数测试 Miller-Rabin算法

    题目描述 Give you a lot of positive integers, just to find out how many prime numbers there are.. In eac ...

  7. 基于visual Studio2013解决C语言竞赛题之0501挑选素数

        题目

  8. 一步一步重写 CodeIgniter 框架 (10) —— 使用 CodeIgniter 类库(续)

    上一节简单实现了 CI 的类库扩展模型,所以 _ci_load_class 和 _ci_init_class 写的不是很完备.根据上节课的分析,当 system/libraries 目录下存在 Ema ...

  9. UNIX网络编程——网络数据包检测

    网络数据包检测 数据包捕获(sniffer):是指在网络上进行数据收集的行为,需要通过网卡来完成. 三种访问方式: BSD Packet Filter(BPF) SVR4 Datalink Provi ...

  10. day8 - isinstance--issubclass-异常处理-自定义异常

    一.isinstance(obj, cls) 检查是否obj是否是类 cls 的对象 __author__ = 'Administrator' class Foo(object): pass obj ...