‎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

原文地址:https://www.cnblogs.com/deepcho/p/cocos2dx-eventtouch-dispatchtouchevent-eventlistenertouch.html