首页 > 代码库 > Cocos2dx引擎10-事件派发

Cocos2dx引擎10-事件派发

本文介绍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-事件派发