cocos2d-x的lua脚本加载CocostudioUI两种方式

前言

  当前版本使用的是quick cocos2dx lua 3.3。UI使用cocostudio编辑器。我们在程序里面可以使用两种方式进行解析UI。开始的时候用的是quick的方法,结果遇到了坑(百分比控件布局,你们可以自己试一下什么效果)。

我在后面简单提一下,不过不是自己遇到的坑,就不知道有多坑。

一.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 或者GUIReader.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 。大家一起学习。

时间: 2024-10-13 18:36:42

cocos2d-x的lua脚本加载CocostudioUI两种方式的相关文章

002-UIImageView和UIButton对比 UIImageView的帧动画 格式符补充 加载图片两种方式 添加删除SUBVIEW

一>.UIImageView和UIButton对比 显示图片 1> UIImageView只是一种图片(图片默认会填充整个UIImageView)  image\setImage: 2> UIButton能显示2种图片 * 背景 (背景会填充整个UIButton)  setBackgroundImage:forState: * 前置(覆盖在背景上面的图片,按照之前的尺寸显示)  setImage:forState: * 还能显示文字 点击事件 1> UIImageView默认是不能

org.apache.hadoop.yarn.conf.ConfigurationProviderFactory分析加载配置文件两种方式

ConfigurationProviderFactory结构如下: /** * Creates an instance of {@link ConfigurationProvider} using given * configuration. * @param bootstrapConf * @return configurationProvider */ @SuppressWarnings("unchecked") public static ConfigurationProvide

xib加载的两种方式

•Xib文件的加载 Ø方法1 NSArray *objs = [[NSBundle mainBundle] loadNibNamed:@"AppView" owner:nil options:nil]; 这个方法会创建xib中的所有对象,并且将对象按顺序放到objs数组中 (如果xib如右图所示,那么objs数组中依次会有3个对象:1个UIView.1个UIButton.1个UISwitch) Ø方法2 bundle参数可以为nil,默认就是main bundle UINib *nib

点评js异步加载的4种方式

主要介绍了点评js异步加载的4种方式,帮助大家更全面的了解js异步加载方式,感兴趣的小伙伴们可以参考一下 js异步加载的4种方式,点评开始. <!DOCTYPE html> <html> <head> <script src="http://common.cnblogs.com/script/jquery.js" type="text/javascript"></script> <script typ

JS异步加载的几种方式

一:同步加载 我们平时使用的最多的一种方式. <script src="http://yourdomain.com/script.js"></script> <script src="http://yourdomain.com/script.js"></script> 同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止后续的解析,只有当当前加载完成,才能进行下一步操作.所以默认同步执行才是安全的.但这样如果js中有输

APP中数据加载的6种方式-b

我们看到的APP,往往有着华丽的启动界面,然后就是漫长的数据加载等待,甚至在无网络的时候,整个处于不可用状态.那么我们怎么处理好界面交互中的加载设计,保证体验无缝衔接,保证用户没有漫长的等待感,而可以轻松自在的享受等待,对加载后的内容有明确的预期呢? 设计师在进行APP设计的设计时,往往会更加专注于界面长什么样,界面和界面之间怎么跳转,给予用户什么样的操作反馈,却偏偏特别容易忽略掉一个比较重要的环节,就是APP数据加载中的设计,所以会导致我们看到的APP,往往有着华丽的启动界面,然后就是漫长的数

iOS APP中数据加载的6种方式

我们看到的APP,往往有着华丽的启动界面,然后就是漫长的数据加载等待,甚至在无网络的时候,整个处于不可用状态.那么我们怎么处理好界面交互中的加载设计,保证体验无缝衔接,保证用户没有漫长的等待感,而可以轻松自在的享受等待,对加载后的内容有明确的预期呢? 设计师在进行APP设计的设计时,往往会更加专注于界面长什么样,界面和界面之间怎么跳转,给予用户什么样的操作反馈,却偏偏特别容易忽略掉一个比较重要的环节,就是APP数据加载中的设计,所以会导致我们看到的APP,往往有着华丽的启动界面,然后就是漫长的数

Flask程序相关配置加载的三种方式

方式一:从对象中加载配置 1.定义配置类,在配置类中添加相应的配置 2.通过app.config.from_object(配置类)进行加载 代码如下: from flask import Flask app=Flask(__name__) # =======从对象中加载配置========= class Config(object): DEBUG=True app.config.from_object(Config) @app.route("/") def index(): retur

javascript脚本异步加载的几种方式

一般而言,javascript脚本一般是建议放在body标签的底部,因为使用script标签加载js时,会停止加载后面的内容而停下来解析脚本并对页面进行渲染,使用src属性加载外部脚本也会造成这样的情况,这样的话,如果在head或者body的前面放入过多的script标签,并且内容很多的时候,会造成页面在解析完所有script标签的内容前有短暂的时间整个页面空白,给用户的体验会很差.但是如果所有的脚本都放在底部,又会造成dom加载完毕后有一段时间页面虽然能看到,但是和用户的交互却很差,因此需要让