关于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, );
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:依旧是没有响应
小结2:
*继续看代码,发现优先级是按照<0, 0, >0排序的(这里和节点的渲染顺序是一样的)
// After sort: priority < 0, > 0
std::sort(fixedListeners->begin(), fixedListeners->end(), [](const EventListener* l1, const EventListener* l2) {
return l1->getFixedPriority() < l2->getFixedPriority();
});
* 所以把优先级改成小于0,应该就可以了
测试2:
eventDispatcher:addEventListenerWithFixedPriority(listener, -)
结果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 = ;
_nodePriorityMap.clear(); 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中的函数,牵涉到了层级关系,zOrder,所以想:设置这个是不是就能达到事件优先级的大小的呢?
测试1:
self:setLocalZOrder(-)
self:setLocalZOrder()
self:setGlobalZOrder(-)
self:setGlobalZOrder()
结果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, > 0
std::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)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 then
print("press image ... ")
end
end
local img = node:getChildByName("Image_46")
img:setTouchEnabled(true);
img:addTouchEventListener(fnTouchImg)
(3)panel和button默认都是添加了触摸事件,而image和label没有,主要原因是cocostudio上面的【交互】是否勾选,然后在加载json文件的时候调用setTouchEnabled函数
//WidgetReader.cpp
widget->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来说,一般都是【交互】没有勾上的原因
关于cocostudio动态添加控件触摸响应无效的学习的更多相关文章
- winform导入导出excel,后台动态添加控件
思路: 导入: 1,初始化一个OpenFileDialog类 (OpenFileDialog fileDialog = new OpenFileDialog();) 2, 获取用户选择文件的后缀名(s ...
- VC中动态添加控件
VC中动态添加控件 动态控件是指在需要时由Create()创建的控件,这与预先在对话框中放置的控件是不同的. 一.创建动态控件: 为了对照,我们先来看一下静态控件的创建. 放置静态控件时必须先建立一个 ...
- 怎样在不对控件类型进行硬编码的情况下在 C#vs 中动态添加控件
文章ID: 815780 最近更新: 2004-1-12 这篇文章中的信息适用于: Microsoft Visual C# .NET 2003 标准版 Microsoft Visual C# .NET ...
- Android 在布局容器中动态添加控件
这里,通过一个小demo,就可以掌握在布局容器中动态添加控件,以动态添加Button控件为例,添加其他控件同样道理. 1.addView 添加控件到布局容器 2.removeView 在布局容器中删掉 ...
- jQuery EasyUI动态添加控件或者ajax加载页面后不能自动渲染问题的解决方法
博客分类: jquery-easyui jQueryAjax框架HTML 现象: AJAX返回的html无法做到自动渲染为EasyUI的样式.比如:class="easyui-layout ...
- asp.net动态添加控件学习
看了老师的教程后,自己一点感悟记录下来: 1.在页面提交后,动态生成的控件会丢失, 但如果生成控件的代码在pageload中,就可以,原理是每次生成页面都执行生成. 2.动态按件或页面原来控件, 在页 ...
- WPF:理解ContentControl——动态添加控件和查找控件
WPF:理解ContentControl--动态添加控件和查找控件 我认为WPF的核心改变之一就是控件模型发生了重要的变化,大的方面说,现在窗口中的控件(大部分)都没有独立的Hwnd了.而且控件可以通 ...
- JQuery动态添加控件并取值
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- WPF 动态添加控件以及样式字典的引用(Style introduction)
原文:WPF 动态添加控件以及样式字典的引用(Style introduction) 我们想要达到的结果是,绑定多个Checkbox然后我们还可以获取它是否被选中,其实很简单,我们只要找到那几个关键的 ...
随机推荐
- Python制作回合制手游外挂简单教程(下)
引入: 接着上篇的博文,今天我们讲如何实现助人为乐 前期准备: 如何获取图片中指定文字的坐标? 我的思路是截取一个小区域,再根据小区域左上角的坐标获取中央坐标 例如: 获取坐上角的x和y坐标,测量x到 ...
- iOS开源项目周报0216
由OpenDigg 出品的iOS开源项目周报第八期来啦.我们的iOS开源周报集合了OpenDigg一周来新收录的优质的iOS开源项目,方便iOS开发人员便捷的找到自己需要的项目工具等.Animated ...
- 小菜读书---《Effective C#:改善C#程序的50种方法》
一.用属性代替可访问的字段 1..NET数据绑定只支持数据绑定,使用属性可以获得数据绑定的好处: 2.在属性的get和set访问器重可使用lock添加多线程的支持. 二.readonly(运行时常量) ...
- SVG 旋转图形实例
本实例展示如何在SVG中画出一个正方形并使之旋转.运行结果如下图所示: 在文本框中输入时间间隔,单位是毫秒.点击Start按钮,蓝色方块就会开始转动,每个时间间隔变化一度.变换的角度在下面的Angle ...
- WCF 创建WCF
一.概述 Windows Communication Foundation(WCF)是由微软发展的一组数据通信的应用程序开发接口,可以翻译为Windows通讯接口,它是.NET框架的一部分.由 .NE ...
- [javaSE] 网络编程(URLConnection)
获取URL对象,new出来,构造参数:String的路径 调用URL对象的openConnection()方法,获取URLConnection对象 调用URLConnection对象的getInput ...
- 九、sparkStream的scala示例
简介 sparkStream官网:http://spark.apache.org/docs/latest/streaming-programming-guide.html#overview spark ...
- Mavean多工程依赖项目
前言 本篇文章基于Java开发小技巧(二):自定义Maven依赖中创建的父工程project-monitor实现,运用我们自定义的依赖包进行多工程依赖项目的开发. 下面以多可执行Jar包项目的开发为例 ...
- vi 多行注释和取消注释
注释 :1,10 s/^/#/ 注释1到10行 取消注释 ctl+v 进入visual block模式 选中 #号 按d 或x 将其删除
- php之连接mssql(sql server)新手教程
ps:网上搜了很多教程,讲的都很好,就是都有点漏的地方,花了一天时间查缺补漏终于弄好了(;´༎ຶД༎ຶ`),希望我的教程能帮到新手,还有写博客的时候因为不小心按错一个键,导致重写了,博客园这个编辑器真 ...