展会信息港展会大全

在cocos2d-js实现自动绑定cocostudioUI控件与事件,cocos2djs事件
来源:互联网   发布日期:2015-09-28 14:43:57   浏览:3573次  

导读: 在cocos2d-js实现自动绑定cocostudioUI控件与事件,cocos2djs事件 一.起因 在客户端游戏开发中最让人恶心的工作就是UI相关的东西,虽然有了像coco...

在cocos2d-js实现自动绑定cocostudioUI控件与事件,cocos2djs事件

一.起因

在客户端游戏开发中最让人恶心的工作就是UI相关的东西,虽然有了像cocostudio这样的可视化工具,但界面中有大量需要由代码访问的控件的时候,需要写太多重复的代码例如:

//加载UI配置文件

var root = ccs.uiReader.widgetFromJsonFile("res/cocosui/UIEditorTest/UIButton_Editor/UIButton_Editor_1.json");

this._mainNode.addChild(root);

//查询back控件,并添事件响应

var back_label = ccui.helper.seekWidgetByName(root, "back");

back_label.addTouchEventListener(this.backEvent,this);

//查询Button_123控件,并添事件响应

var button = root.getChildByName(root, "Button_123");

button.addTouchEventListener(this.touchEvent,this);</span>

上面是最为直接访问控件的方法。问题在于如果一个UI界面中有十个、二十个甚至更多的UI控件需要操作的是候,我们做UI的界面、逻辑开发的同学们是否还有时间愉快的玩耍?有没有什么方法不需要去手写这些代码,就能愉快的访问UI组件与接收UI事件响应呢?

二. 思考

对于手机游戏,特别是卡牌类的游戏来说70%~80%的客户端工作量是在UI布局与逻辑上。

上面的seekWidgetByName、getChildByName、addTouchEventListener函数将大量充斥在客户端代码中,滥竽充数着我们的代码行数。

自己曾经有过Qt的开发经验。Qt中也有自己的UI设计工具,生成xml的ui配置文件. 对于这个xml有两种使用方式:

第一种方式: 使用Qt自己的编译工具,将xml翻译生成一个c++代码文件,代码内容就是根据xml中的信息创建各种控件,设置坐标\属性\事件等。

第二种方式: 在程序中,将xml文件使用UILoader工具类加载进来,成为一个节点。然后调用Qt的函数实现信号/槽的自动关联。实现信号/槽(事件)自动绑定的原理,是要求写一个事件处理函数,格式为:

void on_控件名_信号名(参数);

具体如何使用就不细说了,有兴趣的朋友可以自己去看看。根据Qt的这个功能提示,我们何尝不可以在cocos2d-js自动绑定coccostudio输出的ui文件呢?

三. 名命约定

1. 代码名命约定

根据cocos2d-js代码风格,我们约定:

(1)类成员变量以下划线 "_"开头后面接以驼峰名命格式的英文单词。

例如:_loginButton 、 _closeButton 、 _nameLabel

(2)类中的私有函数也使用同样的方式。

例如:_onLoginButtonTouchBegan: function() { ...}

2. UI命名约定

在cocostudioUI编辑器中,我们遵循上述代码中成员变量的命名规范。将需要由代码访问的控件取名为"_"开头,后面接以驼峰名命格式的英文单词。 请看下图:

3. ccui控件事件命名

ccui.Widget事件注册有两种:

1). 常规的touch事件有:

ccui.Widget.TOUCH_BEGAN触摸开始 (按下)

ccui.Widget.TOUCH_MOVED触摸移动 (移动)

ccui.Widget.TOUCH_ENDED触摸结束 (抬起)ccui.Widget.TOUCH_CANCELED触摸取消 (一般没用)

我们使用widget.addTouchEventListener(selector, target)给控件注册触摸事件,设置回调函数.

2). 控件特殊事件:

比如CheckBox的:

ccui.CheckBox.EVENT_SELECTED

ccui.CheckBox.EVENT_UNSELECTED

又如:TextField的:

ccui.TextField.EVENT_ATTACH_WITH_IME

ccui.TextField.EVENT_DETACH_WITH_IME

ccui.TextField.EVENT_INSERT_TEXT

ccui.TextField.EVENT_DELETE_BACKWARD

这类事件需要使用widget.addEventListener(selector, target)来注册。

这里的selector就是我们的回调函数,需要我们取名并实现这个函数,事件类型是通过参数来识别的:

ctor: function() {

this._super();

...

button.addTouchEventListener(this._onButtonEvent, this);

},

_onButtonEvent: function(sender, type) {

switch(type) {

case ccui.Widget.TOUCH_BEGAN:

...;

return true;

case ccui.Widget.TOUCH_MOVED:

...;

break;

case ccui.Widget.TOUCH_ENDED:

...;

break;

}

}

这个"_onButtonEvent"就是我们为事件函数取的名字,如果我们按:【前缀+控件名(取掉下划线)+事件名】给控件事件函数取名

举例说明:

控件名字为:_button

事件名则为:_onButtonTouchBegan、_onButtonTouchMoved、_onButtonTouchEnded

四. 代码实现

有了上面的约定,我们可以开始实现UI的绑定了。

1. 定义一个自动绑定的控件列表,我们这里列出了常用的控件类型与事件名字。

//触摸事件

sz.UILoader.touchEvents = ["TouchBegan", "TouchMoved", "TouchEnded"];

//控件事件列表

sz.UILoader.widgetEvents = [

//Button

{widgetType: ccui.Button, events: sz.UILoader.touchEvents},

//ImageView

{widgetType: ccui.ImageView, events: sz.UILoader.touchEvents},

//TextFiled

{widgetType: ccui.TextField, events: ["AttachWithIME", "DetachWithIME", "InsertText", "DeleteBackward"]},

//CheckBox

{widgetType: ccui.CheckBox, events: ["Selected", "Unselected"]},

//ListView

{widgetType: ccui.ListView, events:["SelectedItem"]},

//Panel

{widgetType: ccui.Layout, events: sz.UILoader.touchEvents},

//BMFont

{widgetType: ccui.TextBMFont, events: sz.UILoader.touchEvents},

//last must null

null

];

这个sz.UILoader.widgetEvents数组可以根据需要自己添加需要绑定的组件。

2.逻辑流程

1). 使用loader载入ui文件并传入target为当前layer。所有事件和控件变量都将绑定到target上。

2). 遍历载入后的子节点(childNode),检查名字前缀是否以”_”开头。并且该节点类型是否在widgetEvents数组中。3). 将childNode绑定到target上。4). 提取childNode事件函数名,检查target是否有这些函数存在。5). 为widgetNode注册事件响应。

6). 加载类接收到事件响应,转发事件到对应的target的事件处理函数上。

3.UI加载类的具体实现

sz.UILoader = cc.Class.extend({

_eventPrefix: null,

_memberPrefix: null,

/**

* 加载UI文件

* @param target将jsonFile加载出的节点绑定到的目标

* @param jsonFilecocostudio UI编辑器生成的json文件

*/

widgetFromJsonFile: function(target, jsonFile, options) {

cc.assert(target && jsonFile);

if (!options) {

options = {};

}

this._eventPrefix=options.eventPrefix || sz.UILoader.DEFAULT_EVENT_PREFIX;

this._memberPrefix = options.memberPrefix || sz.UILoader.DEFAULT_MEMBER_PREFIX;

var rootNode = ccs.uiReader.widgetFromJsonFile(jsonFile);

if (!rootNode) {

cc.log("Load json file failed");

}

target.rootNode = rootNode;

target.addChild(rootNode);

this._bindMenbers(rootNode, target);

},

/**

* 递归对rootWidget下的子节点进行成员绑定

* @param rootWidget

* @param target

* @private

*/

_bindMenbers: function(rootWidget, target) {

var widgetName,

children = rootWidget.getChildren();

var self = this;

children.forEach(function(widget) {

widgetName = widget.getName();

//控件名存在,绑定到target上

var prefix = widgetName.substr(0, self._memberPrefix.length);

if (prefix === self._memberPrefix) {

target[widgetName] = widget;

self._registerWidgetEvent(target, widget);

}

//绑定子控件,可以实现: a._b._c._d 访问子控件

if (!rootWidget[widgetName]) {

rootWidget[widgetName] = widget;

}

//如果还有子节点,递归进去

if (widget.getChildrenCount()) {

self._bindMenbers(widget, target);

}

});

},

/**

* 获取控件事件

* @param widget

* @returns {*}

*/

_getWidgetEvent: function(widget) {

var bindWidgetEvent = null;

var events = sz.UILoader.widgetEvents;

for (var i = 0; i < events.length; i++) {

bindWidgetEvent = events[i];

if (widget instanceof bindWidgetEvent.widgetType) {

break;

}

}

return bindWidgetEvent;

},

/**

* 注册控件事件

* @param target

* @param widget

* @private

*/

_registerWidgetEvent: function(target, widget) {

var name = widget.getName();

//截取前缀,首字母大写

var newName = name[this._memberPrefix.length].toUpperCase() + name.slice(this._memberPrefix.length + 1);

var eventName = this._eventPrefix + newName + "Event";

var isBindEvent = false;

if (target[eventName]) {

isBindEvent = true;

} else {

//取事控件件名

var widgetEvent = this._getWidgetEvent(widget);

if (!widgetEvent) {

return;

}

//检查事函数,生成事件名数组

var eventNameArray = [];

for (var i = 0; i < widgetEvent.events.length; i++) {

eventName = this._eventPrefix + newName + widgetEvent.events[i];

eventNameArray.push(eventName);

if (cc.isFunction(target[eventName])) {

isBindEvent = true;

}

}

}

//事件响应函数

var self = this;

var eventFunc = function(sender, type) {

var callBack;

if (eventNameArray) {

var funcName = eventNameArray[type];

callBack = target[funcName];

} else {

callBack = target[eventName];

}

if (self._onWidgetEvent) {

self._onWidgetEvent(sender, type);

}

if (callBack) {

return callBack.call(target, sender, type);

}

};

//注册事件监听

if (isBindEvent) {

widget.setTouchEnabled(true);

if (widget.addEventListener) {

widget.addEventListener(eventFunc, target);

} else {

widget.addTouchEventListener(eventFunc, target);

}

}

}

});

sz.uiloader = new sz.UILoader();

所有东西都已经准备好了, 我们看看怎么在具体的代码中使用:

GameLayer = cc.Layer.extend({

ctor: function() {

this._super();

//加载UI文件,绑定控件和事件到this

sz.uiloader.widgetFromJsonFile(this, "res/DemoLogin.ExportJson");

//立即可以访问控件的属性方法了

cc.log(this._closeButton.getName());

},

/*

* _closeButton的TouchBegan事件处理函数

*/

_onCloseButtonTouchBegan: function(sender) {

cc.log("_onCloseButtonTouchBegan");

},

});

现在看看我的客户端代码是不是比之前的简洁多了!

五.前缀的问题

上面花了大量文字讲解了关于命名的问题,有人可能会觉得使用这个UILoader会强奸他的代码。

因为他没有使用“_”做为成员变量前缀,或是成员变量前缀不是“_”而是“m_”。

为了不强奸别人的代码,提供了下面的选项:

sz.uiloader.widgetFromJsonFile(this, "res/DemoLogin.ExportJson", {eventPerfix:"on", memberPrefix:"m_"} );

最后一个可选参数options对象,有两个属性eventPerfix

、memberPrefix用于配置事件前缀和成员变量前缀

六. 前缀+控件名+Event

有些时候,不想将TouchBegan、TouchMoved、TouchEnded分成三个响应函数分别来写,而是使用原来事件参数来判断事件类型。

这时你只需要实现以【前缀+控件名+Event】的函数名例如:

控件名为_loginButton, 定义一个函数如:

_onLoginButtonEvent: function(sender, type) {

switch (type) {

case 0:

cc.log("_onLoginButtonEvent: began");

break;

case 1:

cc.log("_onLoginButtonEvent: move");

break;

case 2:

cc.log("_onLoginButtonEvent: end");

break;

}

},

这时UILoader会优先使用这个事件处理函数,如果同时也实现了一个"_onLoginButtonTouchBegan",它将不会被执行。

这样再次阻止sz.UILoader强奸事件的发生,可以兼容你原来的代码。

完整代码可以到githut下载: https://github.com/ShawnZhang2015/UILoader

http://www.bkjia.com/Androidjc/938635.htmlwww.bkjia.comtruehttp://www.bkjia.com/Androidjc/938635.htmlTechArticle在cocos2d-js实现自动绑定cocostudioUI控件与事件,cocos2djs事件 一.起因 在客户端 游戏开发中最让人恶心的工作就是UI相关的东西,虽然有了像...

赞助本站

人工智能实验室

相关热词: android开发 应用开发

相关内容
AiLab云推荐
推荐内容
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港