time:2015/04/19
1. 描述
* 自己也碰到了在cocostudio上碰到点击按钮没有响应的情况
* 另外,网上有朋友也碰到过,所以自己觉得按照3.0的cocos引擎的新渲染逻辑应该是可以设置的
2. 学习
(1)代码直接调试
动态添加一个按钮或者一个精灵到有cocostudio加载的layout
* 按钮和layout在一个层级,都加载layer上,没有位置上的重叠:结果没有影响,两遍都能响应触摸事件
* 按钮和layout在一个层级,都加载layer上,有位置上的重叠:结果依旧没有影响
* 按钮在layout之前addchild:直接看不见了
* 按钮在layout之后addchild:没有影响
* 按钮属于layout,layout:addChild(),没有位置上的重叠:结果没有影响
* 按钮属于layout,layout:addChild(),有位置上的重叠:结果没有影响
* 添加一个精灵,自己添加触摸事件:
Sprite* sprite = Sprite::create("cocosui/green_edit.png"); bgd->addChild(sprite, 0); auto listener = EventListenerTouchOneByOne::create(); listener->onTouchBegan = CC_CALLBACK_2(UIButtonTest_Editor::onTouchBegan, this); listener->onTouchEnded = CC_CALLBACK_2(UIButtonTest_Editor::onTouchEnded, this); _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
1)localZorder默认为0
* 有影响,具体表现为:点击layout上的按钮有响应;点击精灵sprite也有响应;点击除了layout上的按钮之外所有的地方,包括layout内以及外的点都会响应sprite
* 为什么?因为默认的Button做了区域检测,点击按钮之外不响应;但是自己上面定义的精灵没有做处理,所以原则上会接受到任何触摸的消息
* 点击layout上的按钮的时候,sprite其实是没有接受到触摸消息的,说明什么?第一,layout上的按钮把消息吞掉了;第二,layout上的按钮先于sprite接受到触摸消息
-->关于说明的第一点,看源码: [_touchListener->setSwallowTouches(true);]
void Widget::setTouchEnabled(bool enable) { if (enable == _touchEnabled) { return; } _touchEnabled = enable; if (_touchEnabled) { _touchListener = EventListenerTouchOneByOne::create(); CC_SAFE_RETAIN(_touchListener); _touchListener->setSwallowTouches(true); _touchListener->onTouchBegan = CC_CALLBACK_2(Widget::onTouchBegan, this); _touchListener->onTouchMoved = CC_CALLBACK_2(Widget::onTouchMoved, this); _touchListener->onTouchEnded = CC_CALLBACK_2(Widget::onTouchEnded, this); _touchListener->onTouchCancelled = CC_CALLBACK_2(Widget::onTouchCancelled, this); _eventDispatcher->addEventListenerWithSceneGraphPriority(_touchListener, this); } else { _eventDispatcher->removeEventListener(_touchListener); CC_SAFE_RELEASE_NULL(_touchListener); } }
-->关于第二点看后面的分享-->
2)localZorder为1,bgd->addChild(sprite, 1)
* 同1)
3)sprite的localZOrder为-1
* 同1)
4)sprite->setGlobalZorder(1)
* 同1)
(2)cocos的事件处理顺序
* 看两个接口大致可以看到,处理顺序有优先级之分: <0, 0, >0
addEventListenerWithSceneGraphPriority: 默认为0 addEventListenerWithFixedPriority: 有正负之分了
* 优先级判断的逻辑
void EventDispatcher::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) { // Clear the dirty flag first, if `rootNode` is nullptr, then set its dirty flag of scene graph priority 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) { //默认优先级为0的排序 sortEventListenersOfSceneGraphPriority(listenerID, rootNode); } else { dirtyIter->second = DirtyFlag::SCENE_GRAPH_PRIORITY; } } } }
-->自定义优先级没什么好说的,看排序算法就知道了,按自定义的数值排序;
-->如果相同就按照开始插入的顺序继续,排序算法是不是稳定的还要再看
* 默认为0的优先级排序
visitTarget(rootNode, true); // After sort: priority < 0, > 0 std::sort(sceneGraphListeners->begin(), sceneGraphListeners->end(), [this](const EventListener* l1, const EventListener* l2) { return _nodePriorityMap[l1->getAssociatedNode()] > _nodePriorityMap[l2->getAssociatedNode()]; });
-->这里的排序算法已经不同于自定义的排序算法了,一眼看上去不明觉厉
-->所以要看上面的一个函数,visitTarget,就是这个函数中构造_nodePriorityMap结构的
-->仔细看visitTarget函数其实就是cocos3.0以上的一个渲染顺序的逻辑:globalZorder和globalZorder两个共同决定了渲染顺序,globalZorder高于localZorder,这同时也决定了这个渲染顺序可以让不同的layer上的node有不同的渲染顺序,而且跟parent的layer层级关系没有关系(是这样理解的,但是没有去验证!)
-->总结就是:这里的优先级是先看节点的globalZorder,再看localZorder,如果相同就按照默认加入的顺序。即,先加入的先渲染,先处理事件
3. 问题
从上面的理解来看,cocostudio的按钮在最底层,所以会先接受到触摸消息,并且吞掉,从而其他控件(自定义的)不能接受到触摸消息;但是,自己即使设置了自定义的sprite,还是在studio的按钮之后接收到触摸消息,结果是如果点击的是按钮,自定义控件不会响应!
4. 参考:
[1] http://www.it165.net/pro/html/201405/13283.html