Cocos2d-x 学习笔记(16) 触摸事件与分发 EventTouch dispatchTouchEvent EventListenerTouch
1. EventTouch
触摸事件的成员变量:枚举EventCode、存储Touch的容器。
不同的EventCode代表不同时机的触摸事件,能让监听器调用不同的回调函数。
enum class EventCode
{
BEGAN,
MOVED,
ENDED,
CANCELLED
};
static const int MAX_TOUCHES = 15;
EventCode _eventCode;
std::vector<Touch*> _touches;
这里涉及到了Touch类:
Touch类
封装了一个触摸信息,包括触摸点类型(单点/多点)、坐标、开始坐标、ID、3D触控压力等。有set get方法。
2. EventListenerTouch
触摸事件监听器根据触摸点touch类型的不同,分为单点触摸监听器/多点触摸监听器。
2.1 EventListenerTouchOneByOne
单点触摸监听器回调函数只能处理触摸事件中一个touch。
ID统一为:
const std::string EventListenerTouchOneByOne::LISTENER_ID = "__cc_touch_one_by_one";
该类4个回调函数,根据触摸事件code不同分类:
public:
typedef std::function<bool(Touch*, Event*)> ccTouchBeganCallback;
typedef std::function<void(Touch*, Event*)> ccTouchCallback;
ccTouchBeganCallback onTouchBegan; //开始触摸
ccTouchCallback onTouchMoved; //触摸并移动
ccTouchCallback onTouchEnded; //结束触摸
ccTouchCallback onTouchCancelled; //触摸取消
该类2个成员变量:
private:
std::vector<Touch*> _claimedTouches; //存储处理完BEGAN事件的回调函数的参数touch
bool _needSwallow; //事件需要吞没,为true时停止事件向其他监听器分发,setSwallowTouches(bool)设置,默认为false
在add监听器到分发器时,首先需要对该监听器执行checkAvailable()。单点触摸监听器的checkAvailable()中,要求onTouchBegan需要存在。
2.2 EventListenerTouchAllAtOnce
多点触摸监听器回调函数能一次处理事件中所有touch。
ID为:
const std::string EventListenerTouchAllAtOnce::LISTENER_ID = "__cc_touch_all_at_once";
4个回调函数:
public: typedef std::function<void(const std::vector<Touch*>&, Event*)> ccTouchesCallback; ccTouchesCallback onTouchesBegan; ccTouchesCallback onTouchesMoved; ccTouchesCallback onTouchesEnded; ccTouchesCallback onTouchesCancelled;
checkAvailable()方法要求4个回调函数都要存在。
3. dispatchTouchEvent
在dispatchEvent方法中,当参数事件为TOUCH类型,调用dispatchTouchEvent进行事件分发。
该方法首先对2个触摸监听器ID的容器Vector按优先级排序,之后获取触摸事件中所有touch,用容器保存:
如果2种监听器都存在,isNeedsMutableSet置true。
然后对单点触摸监听器分发事件
单点触摸事件分发的逻辑是,把触摸事件的每个touch依次传给所有排好序的单点触摸监听器,监听器执行定义的匿名函数,而不是监听器自带的_onEvent(单点触摸监听器init时_onEvent置nullptr)。
每个单点触摸监听器执行的回调函数运行流程大致如下:
简而言之,根据触摸事件code决定执行哪种回调函数,开始触摸对应的回调函数处理touch,对处理好的touch存储到监听器容器,以便还是该监听器执行后续的移动触摸/结束触摸的回调。移动触摸对应的回调函数处理touch。结束触摸对应回调函数处理touch,并从监听器touch容器删除开始触摸的touch,说明整个触摸流程结束了。
我们知道,当某个监听器的回调函数返回true时,事件将停止分发到后续监听器。
在单点触摸的监听器中,回调函数返回true时有2个情况:触摸事件停止/触摸事件被回调函数处理完成+已注册+监听器设置true吞没标志。
在单点触摸的监听器中,回调函数返回false时有这些情况:开始触摸的回调函数没有处理完事件/监听器对开始触摸没有回调方法/移动或结束触摸事件分发到无(对应的)开始触摸数据的监听器。
当事件停止,会直接退出当前分发,结束分发。
接下来对多点触摸监听器分发事件
多点触摸事件分发的逻辑是,每个需要多点触摸的touch已经存储在容器mutableTouches中,容器传给所有排好序的多点触摸监听器,监听器执行定义的匿名函数,而不是监听器自带的_onEvent(多点触摸监听器init时_onEvent置nullptr)。
单点触摸是将每个touch依次分发到所有监听器,多点触摸是将所有touch分发到所有监听器。
分发到事件的多点触摸监听器的匿名回调函数:通过对触摸事件code的判断,决定要执行自己监听器的哪个回调函数。当事件停止返回true,停止后续监听器接收事件。
4. 触摸事件分发时的Touch*容器 originalTouches mutableTouches
两个容器开始时都存储了本触摸事件的所有touch。
事件分发到单点触摸监听器,是遍历originalTouches,获取每个touch,再分发touch到监听器。
单点触摸回调函数中,如果一个touch在某个单点触摸监听器中处理完成,且_needSwallow为true,且存在多点触摸监听器,此时要从mutableTouches删除该touch。原因是_needSwallow为true说明本touch处理完后不再继续分发,而mutableTouches是需要分发到多点触摸监听器,故从mutableTouches中删除该touch。mutableTouches中的迭代器自动指向下一位。同时isSwallowed置true,即不用执行++mutableTouchesIter,迭代器已经实现了++操作。
还是上面的情况,如果没有多点触摸监听器,isSwallowed默认false,匿名回调函数返回true,停止本touch分发。接下来执行++mutableTouchesIter,和originalTouches一致。
简而言之,mutableTouches只在有多点触摸监听器的情况下可能发挥它独特的作用(只保存需要接下来多点触发的touch),否则和originalTouches无异,只是存储所有touch而已。
if (isClaimed && listener->_isRegistered && listener->_needSwallow) //如果事件处理完成,监听器注册状态, 监听器吞没 { if (isNeedsMutableSet) //单点多点触摸的监听器都有 { mutableTouchesIter = mutableTouches.erase(mutableTouchesIter); //删除此触摸点信息 isSwallowed = true; // } return true; }
if (!isSwallowed)
++mutableTouchesIter;
//接下来遍历下个touch