关于cocostudio动态添加控件触摸响应无效的学习
time:2015/04/19
1. 描述
* 把studio制作的ui加載之后,動態添加事件(比如說,單點觸摸),結果回調函數(eg:onTouchBegan等)根本沒有響應!
*?另外,網上有朋友也碰到過這個問題,也在網上查到了類似的情況
* 但是,自己覺得按照3.0的cocos引擎的新渲染邏輯應該是可以設置的,而且這個問題也沒有理解清楚,所以自己去看了下源碼研究了一下,做一下記錄,當然能幫到其他人理解就更好了。
2. 學習
(1)直接上代碼看結果
/******說明******/
* layer: 一個基本的層
* layout: cocostudio制作的UI
* 層次關系: layer:addChild(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);(2)那為什么自己會有不響應的問題?
看自己的代碼:
local function onTouchesBegan(touches,event)print("onTouchesBegan ... ") return true end local function onTouchesMoved(touches, event ) print("onTouchesMoved ... ") end local function onToucheEnd(touches,event) print("onToucheEnd ... ") end local listener = cc.EventListenerTouchOneByOne:create() listener:registerScriptHandler(onTouchesBegan,cc.Handler.EVENT_TOUCH_BEGAN) listener:registerScriptHandler(onTouchesMoved,cc.Handler.EVENT_TOUCH_MOVED) listener:registerScriptHandler(onToucheEnd,cc.Handler.EVENT_TOUCH_ENDED) local eventDispatcher = self:getEventDispatcher()eventDispatcher:addEventListenerWithSceneGraphPriority(listener, self)
小結:
*自己是在layer上直接添加了單點觸摸的事件
*用的是addEventListenerWithSceneGraphPriority接口,那么就去看看源碼是怎么處理的?
(3)源代碼分析:FixedPriority
直接看源碼,會發現下面這個函數:
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; } } } }小結1:
* 可以看到在cocos是有事件優先級的,其中addEventListenerWithFixedPriority可以自定義優先級,addEventListenerWithSceneGraphPriority的優先級默認為0
* 既然有優先級的區別,那么毋庸置疑,設置一下就可以了
測試1:
把自己代碼中的最后一行改成下面:
eventDispatcher:addEventListenerWithFixedPriority(listener, 1)結果1:依舊是沒有響應
小結2:
*繼續看代碼,發現優先級是按照<0, 0, >0排序的(這里和節點的渲染順序是一樣的)
// After sort: priority < 0, > 0std::sort(fixedListeners->begin(), fixedListeners->end(), [](const EventListener* l1, const EventListener* l2) {return l1->getFixedPriority() < l2->getFixedPriority();});* 所以把優先級改成小于0,應該就可以了
測試2:
eventDispatcher:addEventListenerWithFixedPriority(listener, -1)結果2:
*果真!有響應了,自己的打印信息出來了,也就是自己的layer在layout之前接收到并且響應了觸摸消息
小結:
* 事件有優先級區別,但是優先級越低,越先接收并且響應消息!所以,如果是cocostudio的ui接收到了觸摸消息,對不起了,后面都不會響應了,因為被吞掉了?[_touchListener->setSwallowTouches(true);]
* 如果優先級都為0(即,SceneGraphPriority),順序又是怎么處理的呢?
(4)源代碼:SceneGraphPriority
首先,看源碼的排序地方
void EventDispatcher::sortEventListenersOfSceneGraphPriority(const EventListener::ListenerID& listenerID, Node* rootNode) {auto listeners = getListeners(listenerID);if (listeners == nullptr)return;auto sceneGraphListeners = listeners->getSceneGraphPriorityListeners();if (sceneGraphListeners == nullptr)return;// Reset priority index_nodePriorityIndex = 0;_nodePriorityMap.clear();visitTarget(rootNode, true);// After sort: priority < 0, > 0std::sort(sceneGraphListeners->begin(), sceneGraphListeners->end(), [this](const EventListener* l1, const EventListener* l2) {return _nodePriorityMap[l1->getAssociatedNode()] > _nodePriorityMap[l2->getAssociatedNode()];});//省略 }*仔細看visitTarget中的函數,牽涉到了層級關系,zOrder,所以想:設置這個是不是就能達到事件優先級的大小的呢?
測試1:
self:setLocalZOrder(-1)self:setLocalZOrder(100)self:setGlobalZOrder(-1)self:setGlobalZOrder(1)結果1:
* self:setLocalZOrder(-1) ---> 無影響
* self:setLocalZOrder(100) ---> 無影響
* self:setGlobalZOrder(-1) ---> 無影響
* self:setGlobalZOrder(1) ---> 有影響
* localZorder為什么沒有影響
* 而設置GlobalZOrder是有影響的
小結1:
* SceneGraphPriority模式下,是按照>0, 0, <0的順序去處理事件節點的(注釋的地方有問題!!!)
visitTarget(rootNode, true);// After sort: priority < 0, > 0std::sort(sceneGraphListeners->begin(), sceneGraphListeners->end(), [this](const EventListener* l1, const EventListener* l2) { return _nodePriorityMap[l1->getAssociatedNode()] > _nodePriorityMap[l2->getAssociatedNode()]; });
* 順序是按照節點的globalZOrder這個值進行排序的,不管是不是child或者parent以及他們之間的所屬關系
* 如果localZOrder的話,都一樣的話就按照原來的順序(即加入節點的順序);另外父節點和子節點的順序是:子節點<0的所有點--->父節點--->子節點大于0 的所有點(所以在這里設置layer的localZOrder沒有用,要設置layout的localZOrder)
?
3. 總結
(1)FixedPriority設置優先級的話,處理順序有優先級之分: <0, 0, >0
(2)SceneGraphPriority模式下,是按照>0, 0, <0的順序去處理事件節點的
4. 參考
[1]?http://www.it165.net/pro/html/201405/13283.html
?
---------------------------------后續學習補充--------------------------------------
1. 關于cocstudio的panel添加觸摸事件無效的研究
時間:2015/05/23
描述:在cocostudio的界面上添加了一個panel,想對這個panel添加觸摸事件,代碼如下:
local listener = cc.EventListenerTouchOneByOne:create();listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN)listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_ENDED)local eventDispatcher = self:getEventDispatcher()eventDispatcher:addEventListenerWithFixedPriority(listener, 1)參考了之前的研究,這里使用了優先級的監聽事件,結果不管是優先級為-1還是1都是不管用的,非常納悶~
學習:
(1)registerScriptHandler
listener的注冊函數,這個是專門為lua寫的接口,可以看到這個函數是在lua_cocos2dx_manual.cpp中,并且是把回調函數放在了c++ 11的function類型中做處理的。
case ScriptHandlerMgr::HandlerType::EVENT_TOUCH_ENDED:{self->onTouchEnded = [=](Touch* touch, Event* event){LuaEventTouchData touchData(touch, event);BasicScriptData data((void*)self,(void*)&touchData);LuaEngine::getInstance()->handleEvent(type, (void*)&data);};ScriptHandlerMgr::getInstance()->addObjectHandler((void*)self, handler, type);}break;通過調試斷點,看了下這個函數的地址(因為一直沒有調用進來,所以看看后面是怎么處理的)。
(2)addEventListenerWithFixedPriority
通過這個接口加到列表中,沒有問題。這里看代碼可以看到開始是加到一個臨時變量中,最后在updateListeners函數中再把listener加到真正處理的列表中的。
(3)dispatchTouchEvent
if (eventCode == EventTouch::EventCode::BEGAN){if (listener->onTouchBegan){isClaimed = listener->onTouchBegan(*touchesIter, event);if (isClaimed && listener->_isRegistered){listener->_claimedTouches.push_back(*touchesIter);}}}在這個函數位置進行處理的listener->onTouchBegan處理。
也就是在這個時候,我開始發現自己一直理解錯了。因為panel本身也是接收觸摸事件的,看代碼可以發現所有的Widget本身就是做了觸摸事件處理的!多此一舉!!!
(4)小結
*?addEventListenerWithFixedPriority依舊是有效的,設置為-1,就會優先執行,但是一定要注意:這個時候的回調函數的參數和onTouchBegan的回調函數參數是完全不一樣的
// 1. addEventListenerWithFixedPriority的回調函數形式 bool Widget::onTouchBegan(Touch *touch, Event *unusedEvent)// 2.addTouchEventListerner的回調函數形式 typedef std::function<void(Ref*,Widget::TouchEventType)> ccWidgetTouchCallback;* 所有的widget自己已經實現了觸摸事件,所以不要多此一舉!只有自己自定義的類,精靈、層等才需要自己去實現和添加。
* 除了觸摸事件,還有FocusEvent,這個要具體去看看。
?
2.?cocstudio中控件的響應
時間:2015/08/27
描述:cocostudio中控件有的可以點擊,有的不可以點擊,panel能遮擋,image和lable不遮擋,這些是為什么?
(1)cocostudio所有的控件都是繼承widget,而widget都是實現了單點觸摸,參見函數:
void Widget::setTouchEnabled(bool enable)(2)所以,image和label都是可以監聽觸摸事件的,不過首先要設置一下,所以他們添加的時候要多設置一下上面的函數
local fnTouchImg = function(sender, eventType)if eventType == ccui.TouchEventType.ended thenprint("press image ... ")endendlocal img = node:getChildByName("Image_46")img:setTouchEnabled(true);img:addTouchEventListener(fnTouchImg)(3)panel和button默認都是添加了觸摸事件,而image和label沒有,主要原因是cocostudio上面的【交互】是否勾選,然后在加載json文件的時候調用setTouchEnabled函數
//WidgetReader.cppwidget->setTag(DICTOOL->getIntValue_json(options, P_Tag));widget->setActionTag(DICTOOL->getIntValue_json(options, P_ActionTag));widget->setTouchEnabled(DICTOOL->getBooleanValue_json(options, P_TouchAble));const char* name = DICTOOL->getStringValue_json(options, P_Name);(4)總結:
* cocostudio下面所有控件(繼承widget)都是有觸摸響應事件的,直接用函數addTouchEventListener即可
* 對于沒有反應的說明setTouchEnabled()是false
* 對于cocostudio來說,一般都是【交互】沒有勾上的原因
轉載于:https://www.cnblogs.com/pk-run/p/4440494.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的关于cocostudio动态添加控件触摸响应无效的学习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 程序打包及签名
- 下一篇: attempt to create de