问题表现
当多点触摸和单点触摸的操作区域有重叠时,如果单点触摸事件设置为吞噬,那么多点触摸无法正常响应,例如:
网络异常,图片无法展示
|
上图中红色区域的多点触摸是无法正常响应的
代码分析
- 鼠标点击事件的源头:组装touch事件,并派发出去
void GLViewImpl::onGLFWMouseCallBack(GLFWwindow* /*window*/, int button, int action, int /*modify*/) { this->handleTouchesBegin(1, &id, &_mouseX, &_mouseY); } void GLView::handleTouchesBegin(int num, intptr_t ids[], float xs[], float ys[]) { EventTouch touchEvent; touchEvent._eventCode = EventTouch::EventCode::BEGAN; auto dispatcher = Director::getInstance()->getEventDispatcher(); dispatcher->dispatchEvent(&touchEvent); } 复制代码
- 具体的派发过程:
void EventDispatcher::dispatchEvent(Event* event) { if (event->getType() == Event::Type::TOUCH) { dispatchTouchEvent(static_cast<EventTouch*>(event)); return; } } void EventDispatcher::dispatchTouchEvent(EventTouch* event) { // 排序 sortEventListeners(EventListenerTouchOneByOne::LISTENER_ID); sortEventListeners(EventListenerTouchAllAtOnce::LISTENER_ID); auto oneByOneListeners = getListeners(EventListenerTouchOneByOne::LISTENER_ID); auto allAtOnceListeners = getListeners(EventListenerTouchAllAtOnce::LISTENER_ID); // 先处理单点 if (oneByOneListeners) { for(auto& touches: originalTouches){ dispatchTouchEventToListeners(listeners, onTouchEvent) } } // 再处理多点,逻辑同上,注意mutableTouches的size if (allAtOnceListeners && mutableTouches.size() > 0) { // ... } } 复制代码
dispatchTouchEventToListeners
派发事件时,会将listener划分为3个批次进行循环执行
- 固定优先级小于0的
- 图形优先级
- 固定优先级大于0的
每个批次一旦执行失败onEvent
,就会跳过这个批次的后续listener
void EventDispatcher::dispatchTouchEventToListeners(EventListenerVector* listeners, const std::function<bool(EventListener*)>& onEvent) // 细节逻辑:固定优先级的会按照优先级依次排序,找到小于0的index for(){ if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l)) { shouldStopPropagation = true; break; } } } 复制代码
- 最关键的就是这个
onEvent
逻辑了,也就是onTouchEvent
:
auto onTouchEvent = [&](EventListener* l) -> bool { // Return true to break EventListenerTouchOneByOne* listener = static_cast<EventListenerTouchOneByOne*>(l); // Skip if the listener was removed. if (!listener->_isRegistered) return false; event->setCurrentTarget(listener->_node); bool isClaimed = false; std::vector<Touch*>::iterator removedIter; EventTouch::EventCode eventCode = event->getEventCode(); if (eventCode == EventTouch::EventCode::BEGAN) { if (listener->onTouchBegan) { // touchBegan返回true,就会将这个touch保存在claimed里面,当touchMove、touchEnd的时候,就能检索到 // 这也解释了touchBegan返回true,后续的move、end才能正常 isClaimed = listener->onTouchBegan(touches, event); if (isClaimed && listener->_isRegistered) { listener->_claimedTouches.push_back(touches); } } } else if (listener->_claimedTouches.size() > 0 && ((removedIter = std::find(listener->_claimedTouches.begin(), listener->_claimedTouches.end(), touches)) != listener->_claimedTouches.end())) { isClaimed = true; switch (eventCode) { case EventTouch::EventCode::MOVED: if (listener->onTouchMoved) { listener->onTouchMoved(touches, event); } break; case EventTouch::EventCode::ENDED: if (listener->onTouchEnded) { listener->onTouchEnded(touches, event); } if (listener->_isRegistered) { listener->_claimedTouches.erase(removedIter); } break; case EventTouch::EventCode::CANCELLED: if (listener->onTouchCancelled) { listener->onTouchCancelled(touches, event); } if (listener->_isRegistered) { listener->_claimedTouches.erase(removedIter); } break; default: CCASSERT(false, "The eventcode is invalid."); break; } } // If the event was stopped, return directly. // 可以在touch回调里面设置停止冒泡: event->stopPropagation() if (event->isStopped()) { updateListeners(event); return true; } CCASSERT(touches->getID() == (*mutableTouchesIter)->getID(), "touches ID should be equal to mutableTouchesIter's ID."); // 当前的listener吞噬触摸点 if (isClaimed && listener->_isRegistered && listener->_needSwallow) { if (isNeedsMutableSet) // 当单点、多点 触摸同时存在 { // 会将当前的touch移除,会影响后续的多点触摸的生效 mutableTouchesIter = mutableTouches.erase(mutableTouchesIter); isSwallowed = true; } return true; } return false; }; 复制代码
影响范围
网络异常,图片无法展示
|