爆裂吧现实

不想死就早点睡

0%

事件分发机制源码解析(一)

Cocos的事件分发机制,怎么说呢,总感觉有些乱,借此整理一下。先看看与事件分发相关的类。  

事件相关的类

Event 相关类, 分发事件的中的事件:

Event(基类), EventCustom(自定义事件), EventTouch(触摸事件), EventMouse(鼠标事件), EventKeyboard(键盘事件), EventFocus(控件获取焦点事件), EventAcceleration(加速计事件)

事件监听器:

EventListener, EventListenerCustom, EventListenerFocus, EventListenerMouse, EventListenerTouch, EventListenerKayboard, EventListenerAcceleration

事件ID

ListenerID(事件的区分标志,其实就是std::string

事件分发器:

EventDispatcher(事件分发机制逻辑集合体)

创建事件

创建事件就简单的new一个Event的子类即可。  

事件监听器的创建与监听

事件监听器,也就是说EventListener。添加事件监听器有三个方法,都在EventDispatch中,分别是:  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/** Adds a event listener for a specified event with the priority of scene graph.
 *  @param listener The listener of a specified event.
 *  @param node The priority of the listener is based on the draw order of this node.
 *  @note  The priority of scene graph will be fixed value 0. So the order of listener item
 *          in the vector will be ' <0, scene graph (0 priority), >0'.
*/
void addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node);

/** Adds a event listener for a specified event with the fixed priority.
 *  @param listener The listener of a specified event.
 *  @param fixedPriority The fixed priority of the listener.
 *  @note A lower priority will be called before the ones that have a higher value.
 *        0 priority is forbidden for fixed priority since it's used for scene graph based priority.
*/
void addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority);
 
/** Adds a Custom event listener. It will use a fixed priority of 1.
* @param eventName A given name of the event.
* @param callback A given callback method that associated the event name.
* @return the generated event. Needed in order to remove the event from the dispatcher
*/
EventListenerCustom* addCustomEventListener(const std::string &eventName, const std::function<void(EventCustom*)>& callback);

 
事件分发的时候主要有两种优先级
    第一种是:ScenePriority
    第二种是:FixedPriority
仔细跳到addEventListenerWithSceneGraphPriority函数去看看会发现:  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)
{
    ...
    // 设置优先级
    listener->setFixedPriority(0);
    ...
 
    addEventListener(listener);
}
 
EventListenerCustom* EventDispatcher::addCustomEventListener(const std::string &eventName, const std::function<void(EventCustom*)>& callback)
{
    EventListenerCustom *listener = EventListenerCustom::create(eventName, callback);\
    // 设置优先级
    addEventListenerWithFixedPriority(listener, 1);
    return listener;
}

 
其实事件监听器保存的优先级其实只有 FixPriority

EventListener这个类的方法中也可以看出来,里面涉及到保存优先级的只有setFixedPriority这一个函数。

说到优先级,那么事件分发的时候是怎么处理这些优先级的呢?官网只有写 SceneGraphPriority 的优先级是怎么处理的,那么 FixPriority 的优先程度和数值是什么关系,这就去偷窥内部了。
 
事件分发优先级顺序:
我们来脱掉 EventDispatcher 的衣服看看:  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void EventDispatcher::dispatchEvent(Event* event)
{
    ...
    // 先通过event获取到事件的标志ListenerID
    auto listenerID = __getListenerID(event);
    // 排序此事件的所有的监听器
    sortEventListeners(listenerID);
    // 分发事件逻辑的函数指针
    auto pfnDispatchEventToListeners = &EventDispatcher::dispatchEventToListeners;
    if (event->getType() == Event::Type::MOUSE) {
        // 如果是鼠标事件重新赋值分发事件的函数指针
        pfnDispatchEventToListeners = &EventDispatcher::dispatchTouchEventToListeners;
    }
    // 获取改事件的所有的监听器
    auto iter = _listenerMap.find(listenerID);
    if (iter != _listenerMap.end())
    {
        // 如果有,取出里面监听器的Vector
        auto listeners = iter->second;
        // 找到对应的监听器的时候会触发的回调函数
        auto onEvent = [&event](EventListener* listener) -> bool{
            event->setCurrentTarget(listener->getAssociatedNode());
            // 触发onEvent回调
            listener->_onEvent(event);
            return event->isStopped();
        };
        // 调用函数指针分发事件
        (this->*pfnDispatchEventToListeners)(listeners, onEvent);
    }
 
    ...
}

 
偷窥代码分析后发现这段代码并没有详细指出分发事件的时候的优先级。仔细想想应该是 sortEventListenerpfnDispatchEventToListeners 中在搞鬼。
先分析 sortEventListener,脱掉她的胖次去里面看看。  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
void EventDispatcher::sortEventListeners(const EventListener::ListenerID& listenerID)
{
    ...
        ...
 
        if ((int)dirtyFlag & (int)DirtyFlag::FIXED_PRIORITY)
        {
            // 对FixPriority优先级类型的监听器排序
            sortEventListenersOfFixedPriority(listenerID);
        }
 
        if ((int)dirtyFlag & (int)DirtyFlag::SCENE_GRAPH_PRIORITY)
        {
            auto rootNode = Director::getInstance()->getRunningScene();
            if (rootNode)
            {
                // 对SceneGraphPriority类型的监听器排序
                sortEventListenersOfSceneGraphPriority(listenerID, rootNode);
            }
            ...
        }
    }
}
 
void EventDispatcher::sortEventListenersOfFixedPriority(const EventListener::ListenerID& listenerID)
{
    auto listeners = getListeners(listenerID);
 
    ...
 
    // After sort: priority < 0, > 0 排序,根据FixedPriority的数值大小,越小的优先级越高
    std::sort(fixedListeners->begin(), fixedListeners->end(), [](const EventListener* l1, const EventListener* l2) {
        return l1->getFixedPriority() < l2->getFixedPriority();
    });
 
    ...
}
 
void EventDispatcher::sortEventListenersOfSceneGraphPriority(const EventListener::ListenerID& listenerID, Node* rootNode)
{
    auto listeners = getListeners(listenerID);
 
    ...
    auto sceneGraphListeners = listeners->getSceneGraphPriorityListeners();
 
    ...
 
    // Reset priority index
    _nodePriorityIndex = 0;
    _nodePriorityMap.clear();
 
    // 从当前根节点rootNode开始,遍历整棵节点树,并标记上相应的节点等级,父节点的优先级比子节点的大
    visitTarget(rootNode, true);
 
    // After sort: priority < 0, > 0 因为SceneGraphPriority都是FixPriority为0的事件类型,所以比较节点在渲染书中的优先级
    std::sort(sceneGraphListeners->begin(), sceneGraphListeners->end(), [this](const EventListener* l1, const EventListener* l2) {
        return _nodePriorityMap[l1->getAssociatedNode()] > _nodePriorityMap[l2->getAssociatedNode()];
    });
 
    ...
}

 
上面写了两种优先级的排序,一种是 FixPriority,优先级根据 fixedPriority 的数值从小往大排序、另一种是 SceneGraphPriority,根据节点在渲染树种的优先级排序,具体怎么样官网有解释,这里不做展开。

值的注意的是 sortEventListener 的时候,判断当前 ListenerID 的类型是用位标记来判断的,一个 int 类型的 flag。也就是说明一个 listenerID 既可以是 SceneGraphPriority 也可以是 FixedPriority,那么实际分发的时候这两个的优先级怎么排?答案就在 pfnDispatchEventToListeners
 
pfnDispatchEventToListeners 可以指向两个方法:dispatchEventToListenersdispatchTouchEventToListeners。其中两个方法除了分发SceneGraphPriority的时候不一样外,其他的一样,为了方便起见,这里只分析 dispatchEventToListeners。  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
void EventDispatcher::dispatchEventToListeners(EventListenerVector* listeners, const std::function<bool(EventListener*)>& onEvent)
{
    bool shouldStopPropagation = false;
    auto fixedPriorityListeners = listeners->getFixedPriorityListeners();
    auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners();
 
    ssize_t i = 0;
    // priority < 0 优先处理priority小于0的时候的事件监听器
    if (fixedPriorityListeners)
    {
        CCASSERT(listeners->getGt0Index() <= static_cast<ssize_t>(fixedPriorityListeners->size()), "Out of range exception!");
 
        if (!fixedPriorityListeners->empty())
        {
            for (; i < listeners->getGt0Index(); ++i)
            {
                auto l = fixedPriorityListeners->at(i);
                // 判断是否可以执行事件,如果可以最后调用onEvent执行,如果onEvent返回true,说明吞噬事件,结束分发。
                if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
                {
                    shouldStopPropagation = true;
                    break;
                }
            }
        }
    }
    // 接下来分发SceneGraphPriority的事件
    if (sceneGraphPriorityListeners)
    {
        // 判断事件是否已经终止发送
        if (!shouldStopPropagation)
        {
            // priority == 0, scene graph priority
            for (auto& l : *sceneGraphPriorityListeners)
            {
                if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
                {
                    shouldStopPropagation = true;
                    break;
                }
            }
        }
    }
    // 最后分发到fixedPriority > 0 的监听器
    if (fixedPriorityListeners)
    {
        if (!shouldStopPropagation)
        {
            // priority > 0
            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;
                }
            }
        }
    }
}

 
这里面清楚的写出了事件分发时候的逻辑处理,先分发事件到 *fixedPriority < 0* 的监听器中,然后再分发到 = 0 的监听器(*SceneGraphPriority*)中,最后在分发到 > 0 的监听器中,如果中途出现 onEvent 返回为 true 的结果,则终止分发。

Ps:这里的 onEvent 调用的其实就是上面 dispatchEvent 代码中的 lambda 表达式。如果想要创建一个触摸事件的优先级比当前所有的触摸事件优先级都高话,只需要把 fixedPriority 的数值设为 < 0 即可。