1. 事件分發方法 EventDispatcher::dispatchEvent(Event* event)
首先通過_isEnabled標志判斷事件分發是否啟用。
執行 updateDirtyFlagForSceneGraph()。把一些node對應的ID置臟標記。
對_inDispatch++,當前正在分發的事件數+1。
DispatchGuard guard(_inDispatch);
接下來是一個判斷,如果是觸摸事件,會調用觸摸專用的分發方法,而不是本方法。
if (event->getType() == Event::Type::TOUCH) { dispatchTouchEvent(static_cast<EventTouch*>(event)); return; }
獲取參數事件的ID作為監聽器ID。
auto listenerID = __getListenerID(event);
接下來對事件同ID的所有監聽器進行排序。
sortEventListeners(listenerID);
又是一個類型判斷,如果是鼠標事件,定義觸摸事件分發函數指針,否則,定義通用的事件分發函數指針。
auto pfnDispatchEventToListeners = &EventDispatcher::dispatchEventToListeners; if (event->getType() == Event::Type::MOUSE) { pfnDispatchEventToListeners = &EventDispatcher::dispatchTouchEventToListeners; }
然后通過參數事件監聽器ID從_listenerMap中找到對應的Vector,該類包含兩個存儲監聽器的容器。
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(); };
進行事件分發。
(this->*pfnDispatchEventToListeners)(listeners, onEvent);
最后對所有待添加和待刪除的監聽器進行處理。
updateListeners(event);
簡而言之,事件分發的邏輯是,通過參數事件,找到事件對應的監聽器ID,分發前還要判斷ID對應的監聽器容器是否需要重新排序,把該事件分發給所有同ID監聽器的回調函數進行處理。
接下來對一些重點方法進行學習。
2. updateDirtyFlagForSceneGraph()
當調用resumeEventListenersForTarget方法,把node的所有關聯監聽器從暫停狀態恢復時,需要把node加入_dirtyNodes。
該函數是就是把_dirtyNodes中的node相關的曾經暫停的監聽器的ID在_priorityDirtyFlagMap置臟標記SCENE_GRAPH_PRIORITY,對這些ID的監聽器容器之后重新排序。
3. DispatchGuard guard(_inDispatch)
創建了DispatchGuard類的對象,_inDispatch作為構造函數。
DispatchGuard(int& count): _count(count) { ++_count; } ~DispatchGuard() { --_count; }
可以看出,對一件事件進行分發時,_inDispatch++。在分發方法結束時,會對這個局部對象析構,_inDispatch--。十分巧妙的實現了對_inDispatch的自動管理。
4. sortEventListeners(listenerID)
簡要的說,在_priorityDirtyFlagMap中判斷每種ID的臟標記,根據臟標記的不同,決定ID的哪些容器要重新排序。
該方法首先獲取待排序的監聽器ID的臟標記。
DirtyFlag dirtyFlag = DirtyFlag::NONE; auto dirtyIter = _priorityDirtyFlagMap.find(listenerID); if (dirtyIter != _priorityDirtyFlagMap.end()) { dirtyFlag = dirtyIter->second; }
臟標記不為NONE,說明容器需要重新排序,於是先把臟標記置NONE,接下來開始排序。臟標記為NONE時,因為已排好序,排序函數執行完成。
if (dirtyFlag != DirtyFlag::NONE) { dirtyIter->second = DirtyFlag::NONE; //...
這里用按位與操作判斷是否對ID的兩個容器排序。根據按位與的結果,可能兩容器都要重新排序,也可能只有一個容器需要排序。
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; } }
對兩個容器排序分別用到了兩個方法:
sortEventListenersOfFixedPriority(listenerID);
sortEventListenersOfSceneGraphPriority(listenerID, rootNode);
4.1 sortEventListenersOfFixedPriority(listenerID)
該方法首先獲取ID對應的fixedListeners容器。
auto listeners = getListeners(listenerID); if (listeners == nullptr) return; auto fixedListeners = listeners->getFixedPriorityListeners(); if (fixedListeners == nullptr) return;
對容器進行排序,按優先級從小到大的順序。
std::stable_sort(fixedListeners->begin(), fixedListeners->end(), [](const EventListener* l1, const EventListener* l2) { return l1->getFixedPriority() < l2->getFixedPriority(); });
對排好序的容器從小到大查找,找到第一個優先級不小於0的監聽器,把其下標記錄,作為Vector的成員_gt0Index。
int index = 0; for (auto& listener : *fixedListeners) { if (listener->getFixedPriority() >= 0) break; ++index; } listeners->setGt0Index(index);
4.2 sortEventListenersOfSceneGraphPriority(listenerID, rootNode)
參數rootNode是當前運行的場景。
同上面的排序一樣,顯先獲取容器。不同之處在於sceneGraphListeners容器里的監聽器優先級都為0,排序需要按照node的順序。
需要_nodePriorityIndex容器記錄node的優先級。
_nodePriorityIndex = 0; _nodePriorityMap.clear(); visitTarget(rootNode, true);
visitTarget方法將計算好的node和node優先級存儲在_nodePriorityMap。接下來對sceneGraphListeners進行排序,排序依照每個監聽器關聯的node在_nodePriorityMap的優先級大小,node優先級大,監聽器排序在前。
std::stable_sort(sceneGraphListeners->begin(), sceneGraphListeners->end(), [this](const EventListener* l1, const EventListener* l2) { return _nodePriorityMap[l1->getAssociatedNode()] > _nodePriorityMap[l2->getAssociatedNode()]; });
4.3 visitTarget(rootNode, true)
簡要的說,將計算好的node和node優先級存儲在_nodePriorityMap
該方法首先對node的子節點排序。排序后子節點按LocalZOrder從小到大排列,相同時按添加到node的順序(即順序不變)。
node->sortAllChildren();
獲取子節點容器,數量。
auto& children = node->getChildren();
auto childrenCount = children.size();
對children進行中序遍歷,遍歷到的node的globalZOrder和node存入_globalZOrderNodeMap中。此時,map中的每個node容器中node都是按LocalZOrder從小到大排列。
if(childrenCount > 0) { Node* child = nullptr; // visit children zOrder < 0 for( ; i < childrenCount; i++ ) { child = children.at(i); if ( child && child->getLocalZOrder() < 0 ) visitTarget(child, false); else break; } if (_nodeListenersMap.find(node) != _nodeListenersMap.end()) { _globalZOrderNodeMap[node->getGlobalZOrder()].push_back(node); } for( ; i < childrenCount; i++ ) { child = children.at(i); if (child) visitTarget(child, false); } } else { if (_nodeListenersMap.find(node) != _nodeListenersMap.end()) { _globalZOrderNodeMap[node->getGlobalZOrder()].push_back(node); } }
場景節點中,先獲取場景中所有節點globalZOrder,並對globalZOrder從小到大排序。
遍歷_globalZOrderNodeMap,獲取每個node。遍歷按globalZOrder從小到大的順序,相同globalZOrder則按先后順序(LocalZOrder從小到大)遍歷。按遍歷的順序,將node依次添加到_nodePriorityMap。優先級按node的順序依次+1。即,越晚繪制的node優先級越高。
if (isRootNode) { std::vector<float> globalZOrders; //存儲scene中所有node的globalZOrder globalZOrders.reserve(_globalZOrderNodeMap.size()); for (const auto& e : _globalZOrderNodeMap) { globalZOrders.push_back(e.first); } std::stable_sort(globalZOrders.begin(), globalZOrders.end(), [](const float a, const float b){ return a < b; }); //globalZOrder從小到大排序 for (const auto& globalZ : globalZOrders) { for (const auto& n : _globalZOrderNodeMap[globalZ]) { _nodePriorityMap[n] = ++_nodePriorityIndex; } } _globalZOrderNodeMap.clear(); }
5. 進行事件分發 dispatchEventToListeners(listeners, onEvent)
函數指針pfnDispatchEventToListeners根據事件ID是否是鼠標類型指向不同的函數。
以非觸摸dispatchEventToListeners為例。
首先獲取ID的兩個監聽器容器:fixedPriorityListeners sceneGraphPriorityListeners。
按照優先級<0 =0 >0的順序,對每個監聽器執行以下代碼。fixedPriorityListeners通過getGt0Index()獲取優先級大於0的監聽器序號為分界點,進行分類。
if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l)) { shouldStopPropagation = true; break; }
這里調用了之前定義的匿名函數onEvent(listener)。
6. 匿名函數 onEvent(listener)
設置事件的_currentTarget為監聽器關聯的node,監聽器執行回調函數_onEvent(event)對事件進行處理。
auto onEvent = [&event](EventListener* listener) -> bool{ event->setCurrentTarget(listener->getAssociatedNode()); listener->_onEvent(event); return event->isStopped(); };
7. 收尾處理 updateListeners(event)
刪除所有待刪除容器里的監聽器。添加所有待添加容器里的監聽器。刪除Vector里isRegistered為false的監聽器。刪除_listenerMap中Vector為空的元素。