当前版本使用的是quick cocos2dx lua 3.3。UI使用cocostudio编辑器。我们在程序里面可以使用两种方式进行解析UI。开始的时候用的是quick的方法,结果遇到了坑(百分比控件布局,你们可以自己试一下什么效果)。
我在后面简单提一下,不过不是自己遇到的坑,就不知道有多坑。
local uiNode = cc.uiloader:load("TestUI.json") self:addChild(uiNode)
cc.uiloader:load("XXXX.json") 后面的参数是你的cocostudio导出文件,注意路径问题
在程序中获取控件的方法,我们可以看下 framework/cc/uiloader/uiloader.lua, (CCSUILoader.lua文件可以看下,讲具体怎么实现的)
-- @module uiloader --[[-- 初始化 cc.uiloader,并提供对外统一接口 cc.uiloader 可以将CCS导出的json文件用quick的纯lua控件构建出UI布局 ]] local UILoaderUtilitys = import(".UILoaderUtilitys") local uiloader = class("uiloader") local CCSUILoader = import(".CCSUILoader") local CCSSceneLoader = import(".CCSSceneLoader") -- start -- -------------------------------- -- 初始化 cc.uiloader,并提供对外统一接口 -- @function [parent=#uiloader] new -- end -- function uiloader:ctor() end -- start -- -------------------------------- -- 解析json文件 -- @function [parent=#uiloader] load -- @param string jsonFile 要解析的json文件 -- @param table params 解析参数 -- @return node#node 解析后的布局 -- end -- function uiloader:load(jsonFile, params) local json if not params or not params.bJsonStruct then local pathInfo = io.pathinfo(jsonFile) if ".csb" == pathInfo.extname then return cc.CSLoader:getInstance():createNodeWithFlatBuffersFile(jsonFile) else json = self:loadFile_(jsonFile) end else json = jsonFile end if not json then print("uiloader - load file fail:" .. jsonFile) return end local node if self:isScene_(json) then node, w, h = CCSSceneLoader:load(json, params) else node, w, h = CCSUILoader:load(json, params) end UILoaderUtilitys.clearPath() return node, w, h end -- start -- -------------------------------- -- 按tag查找布局中的结点 -- @function [parent=#uiloader] seekNodeByTag -- @param node parent 要查找布局的结点 -- @param number tag 要查找的tag -- @return node#node -- end -- function uiloader:seekNodeByTag(parent, tag) if not parent then return end if tag == parent:getTag() then return parent end local findNode local children = parent:getChildren() local childCount = parent:getChildrenCount() if childCount < 1 then return end for i=1, childCount do if "table" == type(children) then parent = children[i] elseif "userdata" == type(children) then parent = children:objectAtIndex(i - 1) end if parent then findNode = self:seekNodeByTag(parent, tag) if findNode then return findNode end end end return end -- start -- -------------------------------- -- 按name查找布局中的结点 -- @function [parent=#uiloader] seekNodeByName -- @param node parent 要查找布局的结点 -- @param string name 要查找的name -- @return node#node -- end -- function uiloader:seekNodeByName(parent, name) if not parent then return end if name == parent.name then return parent end local findNode local children = parent:getChildren() local childCount = parent:getChildrenCount() if childCount < 1 then return end for i=1, childCount do if "table" == type(children) then parent = children[i] elseif "userdata" == type(children) then parent = children:objectAtIndex(i - 1) end if parent then if name == parent.name then return parent end end end for i=1, childCount do if "table" == type(children) then parent = children[i] elseif "userdata" == type(children) then parent = children:objectAtIndex(i - 1) end if parent then findNode = self:seekNodeByName(parent, name) if findNode then return findNode end end end return end -- start -- -------------------------------- -- 按name查找布局中的结点 -- 与seekNodeByName不同之处在于它是通过node的下子结点表来查询,效率更快 -- @function [parent=#uiloader] seekNodeByNameFast -- @param node parent 要查找布局的结点 -- @param string name 要查找的name -- @return node#node -- end -- function uiloader:seekNodeByNameFast(parent, name) if not parent then return end if not parent.subChildren then return end if name == parent.name then return parent end local findNode = parent.subChildren[name] if findNode then -- find return findNode end for i,v in ipairs(parent.subChildren) do findNode = self:seekNodeByName(v, name) if findNode then return findNode end end return end -- start -- -------------------------------- -- 根据路径来查找布局中的结点 -- @function [parent=#uiloader] seekNodeByPath -- @param node parent 要查找布局的结点 -- @param string path 要查找的path -- @return node#node -- end -- function uiloader:seekNodeByPath(parent, path) if not parent then return end local names = string.split(path, '/') for i,v in ipairs(names) do parent = self:seekNodeByNameFast(parent, v) if not parent then return end end return parent end -- start -- -------------------------------- -- 查找布局中的组件结点 -- @function [parent=#uiloader] seekComponents -- @param node parent 要查找布局的结点 -- @param string nodeName 要查找的name -- @param number componentIdx 在查找组件在它的直接父结点的位置 -- @return node#node --[[-- 查找布局中的组件结点 ~~~ lua -- "hero" 是结点名称 -- 1 是 "hero"这个结点下的第一个组件 local hero = cc.uiloader:seekComponents(parentNode, "hero", 1) ~~~ ]] -- end -- function uiloader:seekComponents(parent, nodeName, componentIdx) local node = self:seekNodeByName(parent, nodeName) if not node then return end node = self:seekNodeByName(node, "Component" .. componentIdx) return node end -- private function uiloader:loadFile_(jsonFile) local fileUtil = cc.FileUtils:getInstance() local fullPath = fileUtil:fullPathForFilename(jsonFile) local pathinfo = io.pathinfo(fullPath) UILoaderUtilitys.addSearchPathIf(pathinfo.dirname) local jsonStr = fileUtil:getStringFromFile(fullPath) local jsonVal = json.decode(jsonStr) return jsonVal end function uiloader:isScene_(json) if json.components then return true else return false end end return uiloader
button我们肯定会和他打交道的,这里我说下。在CCSUILoader.lua中我们可以看到,这里加载的是quick自己的button控件
如果我们没有在cocostudio中设置button的选中和禁止图片,这里点击是没有任何效果的,而使用c++时,会点击方法效果,我们可以根据自己的需求做相应修改
function CCSUILoader:createButton(options) local node = cc.ui.UIPushButton.new(self:getButtonStateImages(options), {scale9 = options.scale9Enable, flipX = options.flipX, flipY = options.flipY}) if options.opacity then node:setCascadeOpacityEnabled(true) node:setOpacity(options.opacity) end if options.text then node:setButtonLabel( cc.ui.UILabel.new({text = options.text, size = options.fontSize, color = cc.c3b(options.textColorR, options.textColorG, options.textColorB)})) end if not options.ignoreSize then node:setButtonSize(options.width, options.height) end node:align(self:getAnchorType(options.anchorPointX or 0.5, options.anchorPointY or 0.5), options.x or 0, options.y or 0) return node end
下面是一个读取按钮的例子:
local button = cc.uiloader:seekNodeByPath(self.uiNode, buttonName) if button ~= nil then button:addButtonClickedEventListener(function(...) self:onCickSublistButton() end)
我觉得这个还是比较好的,因为触控一直在更新完善。而quick目前是没有在维护。
local uiNode = ccs.GUIReader:getInstance():widgetFromJsonFile("Test.json")
uiNode:addTo(self)
可以参考GUIReader.lua 或者GUIReader.h (GUIReader.lua 是c++绑定到lua 时生成的api)
这里控件的解析我们使用的是Helper ,可以参考 api Helper.lua 和具体的c++类Helper.h
-------------------------------- -- @module Helper -- @parent_module ccui -------------------------------- -- brief Get a UTF8 substring from a std::string with a given start position and length<br> -- Sample: std::string str = "中国中国中国"; substr = getSubStringOfUTF8String(str,0,2) will = "中国"<br> -- param start The start position of the substring.<br> -- param length The length of the substring in UTF8 count<br> -- return a UTF8 substring -- @function [parent=#Helper] getSubStringOfUTF8String -- @param self -- @param #string str -- @param #unsigned long start -- @param #unsigned long length -- @return string#string ret (return value: string) -------------------------------- -- -- @function [parent=#Helper] changeLayoutSystemActiveState -- @param self -- @param #bool bActive -------------------------------- -- -- @function [parent=#Helper] seekActionWidgetByActionTag -- @param self -- @param #ccui.Widget root -- @param #int tag -- @return Widget#Widget ret (return value: ccui.Widget) -------------------------------- -- Finds a widget whose name equals to param name from root widget.<br> -- param root widget which will be seeked.<br> -- name name value.<br> -- return finded result. -- @function [parent=#Helper] seekWidgetByName -- @param self -- @param #ccui.Widget root -- @param #string name -- @return Widget#Widget ret (return value: ccui.Widget) -------------------------------- -- Finds a widget whose tag equals to param tag from root widget.<br> -- param root widget which will be seeked.<br> -- tag tag value.<br> -- return finded result. -- @function [parent=#Helper] seekWidgetByTag -- @param self -- @param #ccui.Widget root -- @param #int tag -- @return Widget#Widget ret (return value: ccui.Widget) -------------------------------- -- -- @function [parent=#Helper] doLayout -- @param self -- @param #cc.Node rootNode return nil
下面是button的例子:
local button = ccui.Helper:seekWidgetByName(self.uiNode, buttonName)
function CampMainlayer:initButton() local function touchEvent(sender,event) if event == ccui.TouchEventType.ended then if sender ~= nil then local tag = sender:getTag()-1000 --TODO:操作 end end end for i=1,3 do local buttonName = "Button_"..i local button = ccui.Helper:seekWidgetByName(self.uiNode, buttonName) if button ~= nil then button:addTouchEventListener(touchEvent) button:setTag(1000+i) end end end
注意 ccui.TouchEventType.ended 这是在 cocos.ui.GuiConstants中,所以我们想用这些的话,需要 require("cocos.ui.GuiConstants")。
我们在实际的项目开发中,肯定会遇到很多问题,当然,遇到问题我们可以在网上找,但是并不是所有的问题都能找到,所以,自己动手解决问题的能力很重要,
对于我们来说,去看源码,看具体的实现,就能很快找到解决问题的方式。
如果有什么问题,请加我的QQ776274781,或者群:102349463 。大家一起学习。