前言
當前版本使用的是quick cocos2dx lua 3.3。UI使用cocostudio編輯器1.6.0。我們在程序里面可以使用兩種方式進行解析UI。開始的時候用的是quick的方法,
結果遇到了坑(百分比控件布局CCSUILoader.lua在解析時,沒有對百分比進行處理,結果方案可以自己加上去)。
特別提醒:
如果在quick中使用源生的解析方案(c++),可能出現觸摸bug。因為在quick中自己實現了新的觸摸機制(詳情請參考LuaTouchEventManager.h 中具體的實現)
所有的觸摸級別都是0,根據渲染層級處理事件
建議:在項目實際開發中,應該自己封裝一層,方便修改。
一.quick使用cocostudio
1.加載
local uiNode = cc.uiloader:load("TestUI.json") self:addChild(uiNode)
cc.uiloader:load("XXXX.json") 后面的參數是你的cocostudio導出文件,注意路徑問題
2.讀取控件
在程序中獲取控件的方法,我們可以看下 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)
二.cocos2dx自帶使用cocostudio
我覺得這個還是比較好的,因為觸控一直在更新完善。而quick目前是沒有在維護。
1.加載
local uiNode = ccs.GUIReader:getInstance():widgetFromJsonFile("Test.json")
uiNode:addTo(self)
可以參考GUIReader.lua 或者\cocos\editor-support\cocostudio CCSGUIReader.h (GUIReader.lua 是c++綁定到lua 時生成的api)
2.讀取控件
這里控件的解析我們使用的是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 。大家一起學習。
