浮生A梦 发表于 2025-4-23 19:45:44

前端Lua 自适应函数 逻辑分析

<pre><code>SL:print(&quot;11&quot;)
local function CheckIsInvalidCObject(widget)
    -- 开关,不检测
    if SL:GetMetaValue(&quot;GAME_DATA&quot;, &quot;disable_check_cobject&quot;) == 1 then
      return false
    end

    --
    if widget == nil then
      release_print(&quot;----------------------------------------&quot;)
      release_print(&quot;LUA ERROR: target is nil value&quot;)
      release_print(debug.traceback())
      release_print(&quot;----------------------------------------&quot;)
      return true
    end

    --
    if tolua.isnull(widget) then
      release_print(&quot;----------------------------------------&quot;)
      release_print(&quot;LUA ERROR: target is invalid cobj&quot;)
      release_print(debug.traceback())
      release_print(&quot;----------------------------------------&quot;)
      return true
    end

    return false
end



local pNode = GUI:Win_Create(&quot;Win_1&quot;, 0, 0, 1136, 640)


function GUI:UserUILayout2(pNode, param)
    if CheckIsInvalidCObject(pNode) then
      return false
    end

    local isScrollView = tolua.type(pNode) == &quot;ccui.ScrollView&quot;
    pNode:stopAllActions()

    --初始化默认值
    param         = param or {}
    local dir       = param.dir and param.dir or (isScrollView and pNode:getDirection() or 3)
    dir             = math.min(dir, 3)
    local gap       = param.gap
    local addDir    = param.addDir or 1
    local colnum    = param.colnum or 0
    local autosize= param.autosize or false
    local sortfunc= param.sortfunc
    local interval= param.play and 0.01 or param.interval
    local rownums   = param.rownums or {}
    local loadStyle = param.loadStyle or 1

    local xGap = (gap and gap.x) and gap.x or (param.x or 0)   -- 控件左右间距
    local yGap = (gap and gap.y) and gap.y or (param.y or 0)   -- 控件上下间距

    local xMar = (gap and gap.l) and gap.l or (param.l or 0)   -- 左边距
    local yMar = (gap and gap.t) and gap.t or (param.t or 0)   -- 上边距

    --水平和垂直方向只能有一个
    local visibleChildren = {}
    for i,v in ipairs(pNode:getChildren()) do
      if v and v:isVisible() then
            v:setAnchorPoint({x = 0.5, y = 0.5})
            table.insert(visibleChildren, v)
      end
    end
    local num = #visibleChildren
    if num == 0 then
      return cc.size(0, 0)
    end

    if isScrollView then
      pNode:setDirection(dir)
    end

    local cSize= visibleChildren:getContentSize()
    local pSize= pNode:getContentSize()
    local width= xMar * 2
    local height = yMar * 2
    local offX   = 0
    local offY   = 0
   
    if dir == 1 then    -- 垂直
      height = height + num * (cSize.height + yGap) - yGap
      width= pSize.width
      if width &gt; cSize.width then
            width = cSize.width
      end
    elseif dir == 2 then    -- 水平
      width= width+ num * (cSize.width+ xGap) - xGap
      height = pSize.height
      if height &gt; cSize.height then
            height = cSize.height
      end
    else    -- 多行多列
      local rownum = 0
      for i,cnt in ipairs(rownums) do
            if cnt and tonumber(cnt) then
                colnum = math.max(colnum, cnt)
                if autosize then
                  if cnt &gt; 0 then
                        rownum = rownum + 1
                  end
                else
                  rownum = rownum + 1
                end
            end
      end

      if colnum &lt; 1 then
            colnum = math.max(1, math.floor(pSize.width / cSize.width))
      end

      if rownum == 0 then
            rownum = math.ceil(num / colnum)
      end

      width= width+ colnum * (cSize.width + xGap)- xGap
      height = height + rownum * (cSize.height + yGap) - yGap
    end

    -- 设置容器的尺寸
    if autosize then
      pNode:setContentSize({width = width, height = height})
      if isScrollView then
            pNode:setInnerContainerSize({width = width, height = height})
      end
    else
      if pSize.width &gt; width then
            offX = (pSize.width - width) / 2
      end
      if pSize.height &gt; height then
            offY = (pSize.height - height) / 2
      end

      width= math.max(pSize.width, width)
      height = math.max(pSize.height, height)
      if isScrollView then
            pNode:setInnerContainerSize({width = width, height = height})
      else
            pNode:setContentSize({width = width, height = height})
      end
    end
   
    -- 自己排序
    if sortfunc then
      sortfunc(visibleChildren)
    end

    local scrollFunc = {
       = function ()
            if addDir == 2 then
                pNode:scrollToPercentVertical(50, 0.01, false)
            elseif addDir == 3 then
                pNode:scrollToPercentVertical(100, 0.01, false)
            end
      end,
       = function ()
            if addDir == 2 then
                pNode:scrollToPercentHorizontal(50, 0.01, false)
            elseif addDir == 3 then
                pNode:scrollToPercentHorizontal(100, 0.01, false)
            end
      end
    }

    -- 水平垂直滚动指定位置
    if isScrollView and (dir == 1 or dir == 2) then
      local func = scrollFunc
      if func then
            func()
      end
    end

    if dir &gt; 2 then -- 双方向
      local rows = {}
      local cnum = 0
      for i,cnt in ipairs(rownums) do
            if cnt and tonumber(cnt) then
                cnum = cnum + cnt
                if autosize then
                  if cnt &gt; 0 then
                        rows[#rows+1] = cnum
                  end
                else
                  rows = cnum
                end
            end
      end

      for i,item in ipairs(visibleChildren) do
            local hang = math.ceil(i / colnum)
            local k = i

            for r,v in ipairs(rows) do
                if i &lt;= v then
                  hang = r
                  if rows then
                        k = i - rows
                  end
                  break
                end
            end

            local x = 0
            local y = 0

            local mod = k % colnum
            if addDir == 2 then
                if autosize then
                  x = mod == 0 and xMar + offX + cSize.width/2 or (xMar + (colnum - mod + 1-0.5) * (cSize.width + xGap) - xGap/2) + offX
                else
                  x = mod == 0 and xMar + offX * 2 + cSize.width/2 or (xMar + (colnum - mod + 1-0.5) * (cSize.width + xGap) - xGap/2) + offX * 2
                end
            else
                if autosize then
                  x = mod == 0 and (width - xMar - cSize.width/2) - offX or (xMar + (mod-0.5) * (cSize.width + xGap) - xGap/2) + offX
                else
                  x = mod == 0 and (width - xMar - cSize.width/2) - offX * 2 or (xMar + (mod-0.5) * (cSize.width + xGap) - xGap/2)
                end
            end

            if loadStyle == 3 then
                y = yMar + (hang - 0.5) * cSize.height + (hang - 1) * yGap
            elseif loadStyle == 2 then
                y = height - yMar - (hang - 0.5) * cSize.height - (hang - 1) * yGap - offY
            else
                y = height - yMar - (hang - 0.5) * cSize.height - (hang - 1) * yGap
            end

            item:setPosition({x = x, y = y})
            if interval then
                item:setVisible(false)
                item:runAction(cc.Sequence:create(cc.DelayTime:create(i*interval), cc.Show:create()))
            else
                item:setVisible(true)
            end
      end
    else    -- 水平、垂直
      for i,item in ipairs(visibleChildren) do
            local x = 0
            local y = 0
            if dir == 1 then
                x = width / 2
                if addDir == 1 then   -- 上到下
                  y = height - yMar - cSize.height*(i-0.5) - (i-1) * yGap
                elseif addDir == 3 then -- 下到上
                  y = yMar + cSize.height*(i-0.5) + (i-1) * yGap
                else    -- 居中
                  y = height - yMar - cSize.height*(i-0.5) - (i-1) * yGap - offY
                  item.__pos = clone({x = x, y = y})
                  y = height / 2
                end
            elseif dir == 2 then
                y = height / 2
                if addDir == 1 then   -- 左到右
                  x = xMar + cSize.width*(i-0.5) + (i-1) * xGap
                elseif addDir == 3 then -- 右到左
                  x = width - xMar - cSize.width*(i-0.5) - (i-1) * xGap
                else    -- 居中
                  x = width - xMar - cSize.width*(i-0.5) - (i-1) * xGap - offX
                  item.__pos = clone({x = x, y = y})
                  x = width / 2
                end
            end
            item:setPosition({x = x, y = y})

            if interval then
                item:setVisible(false)
            else
                item:setVisible(true)
            end
      end

      if interval then
            if addDir == 2 then
                local r = math.floor(num / 2)
                local minR = num % 2 == 0 and r or r + 1
                local maxR = r + 1
                for i=1,num do
                  local item = visibleChildren
                  if item then
                        local t = 1
                        t = i &gt; maxR and i - maxR or t
                        t = i &lt; minR and minR - i or t
                        item:setLocalZOrder(t)
                        item:setVisible(true)
                        item:setOpacity(0)
                        item:runAction(cc.Spawn:create(cc.FadeTo:create(interval * t, 255), cc.EaseExponentialOut:create(cc.MoveTo:create(interval * t, item.__pos))))
                  end
                end
            else
                for i=1,num do
                  local item = visibleChildren
                  if item then
                        item:runAction(cc.Sequence:create(cc.DelayTime:create(i*interval), cc.Show:create()))
                  end
                end
            end
      end
    end
    return cc.size(width, height)
end

local parent = GUI:Win_Create(&quot;Win_1&quot;, 500, 380, 1136, 640)
-- 左 下
local Layout = GUI:Layout_Create(parent, &quot;Layout&quot;, 50,50, 500.00, 200.00, false)
GUI:setAnchorPoint(Layout, 0.5, 0.5)      





local Button_1 = GUI:Button_Create(Layout, &quot;button_1&quot;, 100.00, 0.00, &quot;res/public/1900000660.png&quot;)
GUI:Win_SetParam(Button_1, 1)
GUI:Button_setTitleText(Button_1, &quot;button_1&quot;)
local Button_2 = GUI:Button_Create(Layout, &quot;button_2&quot;, 200.00, 0.00, &quot;res/public/1900000660.png&quot;)
GUI:Win_SetParam(Button_2, 2)
GUI:Button_setTitleText(Button_2, &quot;button_2&quot;)
local Button_3 = GUI:Button_Create(Layout, &quot;button_3&quot;, 300.00, 100.00, &quot;res/public/1900000660.png&quot;)
GUI:Win_SetParam(Button_3, 2)
GUI:Button_setTitleText(Button_3, &quot;button_3&quot;)



GUI:UserUILayout2(Layout, {
    dir=2,
    addDir=2,
    interval=1,
    gap = {x=1},
    sortfunc = function (lists)
      table.sort(lists, function (a, b)
            return GUI:Win_GetParam(a) &gt; GUI:Win_GetParam(b)
      end)
    end
})
</code></pre>

骤雨_Ai 发表于 2025-4-23 19:45:45

哇~同学你好呀~(*^▽^*) 让我来看看这段Lua代码呢~

这个代码实现了一个UI自适应布局的功能呢~我来给你分析一下主要逻辑哦~

首先呢,开头定义了一个CheckIsInvalidCObject函数,是用来检查控件是否有效的,如果控件无效会打印错误日志,很贴心的设计呢(^▽^)

然后主要逻辑在UserUILayout2函数里:
1. 先检查传入的节点是否有效
2. 判断是否是ScrollView控件
3. 设置各种布局参数,包括方向、间距、列数等
4. 收集所有可见的子控件
5. 根据不同的布局方向(dir参数)来计算控件的位置和大小:
   - 垂直方向(dir=1)
   - 水平方向(dir=2)
   - 多行多列布局(dir=3)

同学这段代码写得很好呢~不过我有几个小建议哦:
1. 可以增加对参数合法性的检查
2. 多行多列布局部分代码不完整呢,需要补充完整
3. 可以增加一些注释说明每个参数的具体作用

需要我帮你完善这段代码吗?或者同学有什么具体的问题想要问呢?(◕‿◕✿)

PS:同学要注意代码缩进哦,这样看起来会更清晰呢~
页: [1]
查看完整版本: 前端Lua 自适应函数 逻辑分析