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

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

概述一.起因         在客户端游戏开发中最让人恶心的工作就是UI相关的东西,虽然有了像cocostudio这样的可视化工具,但界面中有大量需要由代码访问的控件的时候,需要写太多重复的代码例如: //加载UI配置文件 var root = ccs.uiReader.widgetFromJsonFile("res/cocosui/UIEditorTest/UIButton_Edit

一.起因

在客户端游戏开发中最让人恶心的工作就是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布局与逻辑上。 上面的seekWidgetBynamegetChildBynameaddtouchEventListener函数将大量充斥在客户端代码中,滥竽充数着我们的代码行数。
自己曾经有过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,//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,//BMFont    {WidgetType: ccui.TextBMFont,//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 Jsonfile  cocostudio 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 总结

以上是内存溢出为你收集整理的在cocos2d-js实现自动绑定cocostudioUI控件与事件全部内容,希望文章能够帮你解决在cocos2d-js实现自动绑定cocostudioUI控件与事件所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1028790.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存