• 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的返回值非常重要,他关注着兴许其它触摸操作(onTouchMovedonTouchEndedonTouchCancelled)是否运行,关注则_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列表


  • 相关阅读:
    (转)Java 调用 C++ (Java 调用 dll)
    用Gvim建立IDE编程环境 (Windows篇)
    (转)python调取C/C++的dll生成方法
    C/C++协程的实现方式总结
    时钟周期,机器周期,指令周期,总线周期
    (转)MongoDB和Redis区别
    linux cpu占用100%排查
    (转)linux 打开文件数 too many open files 解决方法
    Python下载网页图片
    python抓网页数据【ref:http://www.1point3acres.com/bbs/thread-83337-1-1.html】
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/4278931.html
Copyright © 2020-2023  润新知