• 使用 Bolt 实现 GridView 表格控件


    用 Bolt 实现了一个表格控件:

    1. 提供 Insert,Remove,Get,Set 接口,可以为表格增删数据;

    2. 通过  ItemClass, ItemSetDataFunc 属性来指定显示数据所用的 itemObj;

    3. 不会每个 data 都创建 itemObj 来显示, 只为需要显示的数据创建 itemObj;

    4. 提供 AttachItemEvent 接口,可以监听 itemObj 向外发出的事件;

    5. 根据屏幕大小,列数可以自适应;

    6. 根据屏幕大小,每一列的宽度可以自适应;


    gridview.xml

    <xlue>
        <control class="GridView">
            <attr_def>
                <attr name="ItemClass" type="string" />            <!-- 子控件类名 -->
                <attr name="ItemWidth" type="int" />               <!-- 子控件宽度 -->
                <attr name="ItemHeight" type="int" />              <!-- 子控件高度 -->
                <attr name="ItemSetDataFunc" type="string" />      <!-- 子控件 SetData 函数名,GridView 自动调用子控件的 SetData 函数来为子控件设定数据 -->
            
                <attr name="ColumnNum" type="int">
                    <default>1</default>
                </attr>
                <attr name="AutoColumnCount" type="bool">          <!-- 列数根据 GridView 控件宽度自适应, 为 true 则 ColumnNum, AutoColumnWidth 属性失效 -->
                    <default>false</default>
                </attr>
                <attr name="AutoColumnWidth" type="bool">          <!-- 子控件的宽度根据 GridView 控件宽度自适应, 为 true 则 ItemWidth 属性失效 -->
                    <default>false</default>
                </attr>
                
                <attr name="ColumnSpace" type="int">               <!-- 列与列之间的间隙 -->
                    <default>0</default>
                </attr>
                <attr name="RowSpace" type="int">                  <!-- 行与行之间的间隙 -->
                    <default>0</default>
                </attr>
                
                <attr name="ScrollBarBkg" type="string" />                 <!-- 滚动条背景 texture id -->
                <attr name="ScrollBarSliderNormal" type="string" />        <!-- 滚动条滑块 normal 态 texture id -->
                <attr name="ScrollBarSliderHover" type="string" />         <!-- 滚动条滑块 hover 态 texture id -->
                <attr name="ScrollBarSliderDown" type="string" />          <!-- 滚动条滑块 down 态 texture id -->
            </attr_def>
            <method_def>
                <ResetData  file="GridView.xml.lua" func="ResetData" />
                <InsertData file="GridView.xml.lua" func="InsertData" />
                <RemoveData file="GridView.xml.lua" func="RemoveData" />
                
                <SetData file="GridView.xml.lua" func="SetData" />
                <GetData file="GridView.xml.lua" func="GetData" />
                
                <AttachItemEvent file="GridView.xml.lua" func="AttachItemEvent" /> <!-- 监听子控件的事件 -->
                <DetachItemEvent file="GridView.xml.lua" func="DetachItemEvent" /> <!-- 取消监听子控件的事件 -->
            </method_def>
            <event_def>
            </event_def>
            <objtemplate>
                <children>
                    <obj id="container" class="LayoutObject">
                        <attr>
                            <left>0</left>
                            <top>0</top>
                            <width>father.width</width>
                            <height>father.height</height>
                            <limitchild>1</limitchild>
                        </attr>
                        <eventlist>
                            <event name="OnPosChange" file="GridView.xml.lua" func="Container_OnPosChange" />
                            <event name="OnMouseWheel" file="GridView.xml.lua" func="Container_OnMouseWheel" />
                        </eventlist>
                    </obj>
                    <obj id="vscrollbar.bkg" class="TextureObject">
                        <attr>
                            <left>father.width-12</left>
                            <top>0</top>
                            <width>12</width>
                            <height>father.height</height>
                            <zorder>100</zorder>
                        </attr>
                        <children>
                            <obj id="vscrollbar.slider" class="TextureObject">
                                <attr>
                                    <left>2</left>
                                    <top>0</top>
                                    <width>8</width>
                                    <height>0</height>
                                    <zorder>1000</zorder>
                                </attr>
                                <eventlist>
                                    <event name="OnLButtonDown" file="GridView.xml.lua" func="VScrollBar_OnLButtonDown" />
                                    <event name="OnLButtonUp"   file="GridView.xml.lua" func="VScrollBar_OnLButtonUp" />
                                    <event name="OnMouseMove"   file="GridView.xml.lua" func="VScrollBar_OnMouseMove" />
                                    <event name="OnMouseLeave" file="GridView.xml.lua"  func="VScrollBar_OnMouseLeave" />
                                    <event name="OnMouseWheel" file="GridView.xml.lua" func="Container_OnMouseWheel" redirect="father:container" />
                                </eventlist>
                            </obj>
                        </children>
                    </obj>
                </children>
                <eventlist>
                    <event name="OnInitControl" file="GridView.xml.lua" func="OnInitControl" />
                    <event name="OnDestroy" file="GridView.xml.lua" func="OnDestroy" />
                </eventlist>
            </objtemplate>
        </control>
    </xlue>

    gridview.xml.lua

    ------------------------------- 以下是外部函数        --------------------------------
    
    -- 重置列表
    function ResetData(ctrlObj, dataList)
        local attr = ctrlObj:GetAttribute()
        
        -- 列表置顶
        attr.VirtualTop = 0
        
        -- 先清空,再添加新数据
        ctrlObj:RemoveData(1, #attr.IdexToDataMap)
        ctrlObj:InsertData(1, dataList)
    end
    
    -- 在列表指定位置插入数据
    function InsertData(ctrlObj, insertIndex, dataList)
        local attr = ctrlObj:GetAttribute()
        
        if insertIndex == nil or dataList == nil then
            return
        end
        if type(dataList) ~= "table" or #dataList == 0 then
            return
        end
        if insertIndex < 1 or insertIndex > #attr.IdexToDataMap + 1 then
            return
        end
        
        -- 更新 IdexToDataMap 表
        for i, data in ipairs(dataList) do
            local dataInfo = {}
            dataInfo.data = data   -- data 可能重复,所以不能当作 key, 这里把 data 放到 dataInfo 里面,就可以用 dataInfo 作 key 了
            table.insert(attr.IdexToDataMap, insertIndex+i-1, dataInfo)
        end
        
        -- 更新 ItemToIdexMap 表
        -- 因为插入了新数据, insertIndex 后面的 dataIndex 增加了 #dataList
        for itemId, dataIndex in pairs(attr.ItemToIdexMap) do
            if dataIndex >= insertIndex then
                attr.ItemToIdexMap[itemId] = dataIndex + #dataList
            end
        end
        
        RefreshUI(ctrlObj)
    end
    
    -- 删除数据
    function RemoveData(ctrlObj, beginIndex, endIndex)
        local attr = ctrlObj:GetAttribute()
        
        if beginIndex == nil or endIndex == nil then
            return
        end
        if beginIndex < 1 or beginIndex > #attr.IdexToDataMap then
            return
        end
        if endIndex < beginIndex or endIndex > #attr.IdexToDataMap then
            return
        end
        
        -- 更新 IdexToDataMap 表
        local removedDataList = {}
        for i=beginIndex, endIndex do
            table.insert(removedDataList, attr.IdexToDataMap[beginIndex])
            table.remove(attr.IdexToDataMap, beginIndex)
        end
        
        -- 更新 DataToItemMap 表
        local removedItemList = {}
        for _, removedDataInfo in ipairs(removedDataList) do
            if attr.DataToItemMap[removedDataInfo] then
                table.insert(removedItemList, attr.DataToItemMap[removedDataInfo])
                attr.DataToItemMap[removedDataInfo] = nil
            end
        end
        
        -- 更新 ItemToIdexMap 表
        for _, removedItemId in ipairs(removedItemList) do
            if attr.ItemToIdexMap[removedItemId] then
                -- 删掉不用的项
                attr.ItemToIdexMap[removedItemId] = nil
                RecycleItemObj(ctrlObj, removedItemId)
            end
        end
        for itemId, dataIndex in pairs(attr.ItemToIdexMap) do
            if dataIndex >= endIndex then
                -- 因为删除了旧数据, endIndex 后面的 dataIndex 减少了 endIndex-beginIndex+1
                attr.ItemToIdexMap[itemId] = dataIndex - (endIndex-beginIndex+1)
            end
        end
        
        RefreshUI(ctrlObj)
    end
    
    -- 设置数据
    function SetData(ctrlObj, dataIndex, data)
        local attr = ctrlObj:GetAttribute()
        
        -- 更新数据
        if attr.IdexToDataMap[dataIndex] == nil then
            return
        end
        attr.IdexToDataMap[dataIndex].data = data
        
        -- 更新界面
        local dataInfo = attr.IdexToDataMap[dataIndex]
        local itemId = attr.DataToItemMap[dataInfo]
        if itemId == nil then
            return
        end
        SetItemData(ctrlObj, itemId, data)
    end
    
    -- 获取数据
    function GetData(ctrlObj, dataIndex)
        local attr = ctrlObj:GetAttribute()
        
        local dataInfo = attr.IdexToDataMap[dataIndex]
        if dataInfo == nil then
            return
        end
        
        return dataInfo.data
    end
    
    -- 监听 itemObj 发出的事件
    function AttachItemEvent(ctrlObj, eventName, callback)
        local attr = ctrlObj:GetAttribute()
        
        if eventName == nil or callback == nil then
            return
        end
        
        if attr.EventCookieMap[eventName] == nil then
            attr.EventCookieMap[eventName] = {}
        end
        
        -- 分配 cookie
        local cookie = attr.EventCookieMap.CookieNum
        attr.EventCookieMap.CookieNum = cookie + 1
        -- 记录到 EventCookieMap 中
        attr.EventCookieMap[eventName][cookie] = callback
        
        if attr.ItemCookieMap[eventName] ~= nil then
            -- ItemCookieMap[eventName] 不为空,说明 itemObj 已经监听了这个事件
            return cookie
        end
        
        -- 为每个 itemObj 设置 eventName 的监听器
        attr.ItemCookieMap[eventName] = {}
        for _, itemId in pairs(attr.DataToItemMap) do
            local itemObj = ctrlObj:GetControlObject(itemId)
            local itemCookie = itemObj:AttachListener(eventName, true, function(...)
                OnItemEvent(eventName, select(1, ...))
            end)
            
            -- 把 itemId 和 cookie 的对应关系记到表里面
            attr.ItemCookieMap[eventName][itemId] = itemCookie
        end
        
        return cookie
    end
    
    -- 取消监听器
    function DetachItemEvent(ctrlObj, eventName, cookie)
        local attr = ctrlObj:GetAttribute()
        
        if eventName == nil or cookie == nil then
            return
        end
        if attr.EventCookieMap[eventName] == nil then
            return
        end
        
        -- 去除 cookie 对应的监听器
        attr.EventCookieMap[eventName][cookie] = nil
        
        local count = 0
        for cookie, callback in pairs(attr.EventCookieMap[eventName]) do
            count = count + 1
        end
        if count > 0 then
            return
        end
        
        -- 如果已经没有人监听这个事件了,那么 itemObj 也不用监听这个事件了
        if attr.ItemCookieMap[eventName] == nil then
            return
        end
        for itemId, itemCookie in pairs(attr.ItemCookieMap[eventName]) do
            local itemObj = ctrlObj:GetControlObject(itemId)
            itemObj:RemoveListener(eventName, itemCookie)
        end
        
        attr.ItemCookieMap[eventName] = nil
        attr.EventCookieMap[eventName] = nil
    end
    
    ------------------------------- 以下是事件函数        --------------------------------
    
    function OnInitControl(ctrlObj)
        local attr = ctrlObj:GetAttribute()
        
        -- 用三个 map 来记录 dataIndex, dataInfo, itemId 三者的关联,便于互相之间快速索引
        attr.IdexToDataMap = {}
        attr.DataToItemMap = {}
        attr.ItemToIdexMap = {}
    
        -- 虚拟 Top ,指的是列表的第一个 dataInfo 在界面中应该处于的位置,依据这个来计算每个 dataInfo 的 top
        attr.VirtualTop = 0
        
        -- 回收不用的 itemObj
        attr.RecycleItemList = {}
        attr.ItemIdCache = {}
        attr.ItemIdMax = 0
        
        -- 记录外部调用 AttachItemEvent 时分配的 cookie 和 callback
        attr.EventCookieMap = {}
        attr.EventCookieMap.CookieNum = 0  -- cookie 的分配就简单地 +1 好了
        -- 记录每个 itemObj 关注事件所返回的 cookie
        attr.ItemCookieMap = {}
        
        if attr.ScrollBarBkg ~= nil then
            local vsbarBkgObj = ctrlObj:GetControlObject("vscrollbar.bkg")
            vsbarBkgObj:SetTextureID(attr.ScrollBarBkg)
        end
        if attr.ScrollBarSliderNormal ~= nil then
            local vsbarObj = ctrlObj:GetControlObject("vscrollbar.slider")
            vsbarObj:SetTextureID(attr.ScrollBarSliderNormal)
        end
    end
    
    function OnDestroy(ctrlObj)
    
    end
    
    function Container_OnPosChange(containerObj, oldLeft, oldTop, oldRight, oldBottom, newLeft, newTop, newRight, newBottom)
        local ctrlObj = containerObj:GetOwnerControl()
        
        local oldWidth, oldHeight = oldRight-oldLeft, oldBottom-oldTop
        local newWidth, newHeight = newRight-newLeft, newBottom-newTop
        if newWidth == oldWidth and newHeight == oldHeight then
            -- 控件大小未改变,不需要需要刷新
            return
        end
        
        RefreshUI(ctrlObj)
    end
    
    function Container_OnMouseWheel(containerObj, x, y, distance, flags)
        local ctrlObj = containerObj:GetOwnerControl()
        local attr = ctrlObj:GetAttribute()
        
        local moveDistance = distance / 5
        attr.VirtualTop = attr.VirtualTop + moveDistance
        
        RefreshUI(ctrlObj)
    end
    
    function VScrollBar_OnLButtonDown(vsbarObj, x, y, flags)
        local ctrlObj = vsbarObj:GetOwnerControl()
        local attr = ctrlObj:GetAttribute()
        
        vsbarObj:SetCaptureMouse(true)
        
        if attr.ScrollBarSliderDown then
            vsbarObj:SetTextureID(attr.ScrollBarSliderDown)
        end
        
        attr.VScrollBar_LButtonDown_PosY = y
    end
    
    function VScrollBar_OnLButtonUp(vsbarObj, x, y, flags)
        local ctrlObj = vsbarObj:GetOwnerControl()
        local attr = ctrlObj:GetAttribute()
        
        vsbarObj:SetCaptureMouse(false)
        
        if attr.ScrollBarSliderNormal then
            vsbarObj:SetTextureID(attr.ScrollBarSliderNormal)
        end
        
        attr.VScrollBar_LButtonDown_PosY = 0
    end
    
    function VScrollBar_OnMouseMove(vsbarObj, x, y, flags)
        local ctrlObj = vsbarObj:GetOwnerControl()
        local attr = ctrlObj:GetAttribute()
        
        if flags == 1 then         -- 鼠标左键被按下
            local moveDistance = y - attr.VScrollBar_LButtonDown_PosY
            moveDistance = - moveDistance
            attr.VirtualTop = attr.VirtualTop + moveDistance
            
            RefreshUI(ctrlObj)
        else
            if attr.ScrollBarSliderHover then
                vsbarObj:SetTextureID(attr.ScrollBarSliderHover)
            end
        end
    end
    
    function VScrollBar_OnMouseLeave(vsbarObj, x, y)
        local ctrlObj = vsbarObj:GetOwnerControl()
        local attr = ctrlObj:GetAttribute()
        
        if attr.ScrollBarSliderNormal then
            vsbarObj:SetTextureID(attr.ScrollBarSliderNormal)
        end
    end
    
    ------------------------------- 以下是私有函数        --------------------------------
    
    function RefreshUI(ctrlObj)
        -- 调整 列数
        AdjustColumnNum(ctrlObj)
        
        -- 调整 列宽度
        AdjustColumnWidth(ctrlObj)
        
        -- 调整 VirtualTop
        AdjustVirtualTop(ctrlObj)
    
        -- 重新绑定 data 到 itemObj
        RebindDataToItem(ctrlObj)
        
        -- 调整 itemObj 的位置
        AdjustItemPos(ctrlObj)
        
        -- 调整滚动条位置
        AdjustVScrollBarPos(ctrlObj)
    end
    
    function AdjustColumnNum(ctrlObj)
        local attr = ctrlObj:GetAttribute()
        local containerObj = ctrlObj:GetControlObject("container")
        
        local l,t,r,b = containerObj:GetObjPos()
        local containerWidth, containerHeight = r-l, b-t
        
        if attr.AutoColumnCount == true then
            attr.ColumnNum = math.floor(containerWidth / (attr.ItemWidth+attr.ColumnSpace))
        end
    end
    
    function AdjustColumnWidth(ctrlObj)
        local attr = ctrlObj:GetAttribute()
        local containerObj = ctrlObj:GetControlObject("container")
        
        local l,t,r,b = containerObj:GetObjPos()
        local containerWidth = r-l
        
        if attr.AutoColumnCount == false then
            if attr.AutoColumnWidth == true then
                attr.ItemWidth = math.floor((containerWidth+attr.ColumnSpace) / attr.ColumnNum)
            end
        end
    end
    
    function AdjustVirtualTop(ctrlObj)
        local attr = ctrlObj:GetAttribute()
        local containerObj = ctrlObj:GetControlObject("container")
    
        local virtualBottom = attr.VirtualTop + math.ceil(#attr.IdexToDataMap / attr.ColumnNum) * (attr.ItemHeight+attr.RowSpace)
        local l,t,r,b = containerObj:GetObjPos()
        local containerHeight = b-t
        
        -- 不要让列表底部有间隙
        if virtualBottom < containerHeight then
            attr.VirtualTop = attr.VirtualTop + containerHeight - virtualBottom
        end
        -- 不要让列表顶部有间隙
        if attr.VirtualTop > 0 then
            attr.VirtualTop = 0
        end
    end
    
    function RebindDataToItem(ctrlObj)
        local attr = ctrlObj:GetAttribute()
        local containerObj = ctrlObj:GetControlObject("container")
        
        -- 1. 计算哪些 dataInfo 处于可视范围
        local visibleDataMap = {}
        local l,t,r,b = containerObj:GetObjPos()
        local containerHeight = b-t
        local beginRow = math.floor( (0-attr.VirtualTop) / (attr.ItemHeight+attr.RowSpace) )
        local endRow = math.ceil( (containerHeight-attr.VirtualTop) / (attr.ItemHeight+attr.RowSpace) ) + 1
        local beginIndex = beginRow * attr.ColumnNum + 1
        local endIndex = endRow * attr.ColumnNum
        beginIndex = math.min(beginIndex, #attr.IdexToDataMap)
        endIndex = math.min(endIndex, #attr.IdexToDataMap)
    
        if beginIndex > 0 and endIndex >= beginIndex then
            for dataIndex=beginIndex, endIndex do
                local dataInfo = attr.IdexToDataMap[dataIndex]
                visibleDataMap[dataInfo] = dataIndex
            end
        end
        
        -- 2. 回收不用的 itemObj
        local inVisibleDataMap = {}
        for dataInfo, itemId in pairs(attr.DataToItemMap) do
            if visibleDataMap[dataInfo] == nil then
                inVisibleDataMap[dataInfo] = itemId
            end
        end
        for dataInfo, itemId in pairs(inVisibleDataMap) do
            attr.DataToItemMap[dataInfo] = nil
            attr.ItemToIdexMap[itemId] = nil
            RecycleItemObj(ctrlObj, itemId)
        end
        
        -- 3. 为没有绑定 itemObj 的 dataInfo 进行绑定
        for dataInfo, dataIndex in pairs(visibleDataMap) do
            if attr.DataToItemMap[dataInfo] == nil then
                local itemObj = CreateItemObj(ctrlObj)
                local itemId = itemObj:GetID()
                
                attr.DataToItemMap[dataInfo] = itemId
                attr.ItemToIdexMap[itemId] = dataIndex
                
                -- 初次绑定的 itemObj, 要调用 SetDataFunc
                SetItemData(ctrlObj, itemId, dataInfo.data)
            end
        end
        
        -- 4. 清空不用的 itemObj
        CleanItemObj(ctrlObj)
    end
    
    function AdjustItemPos(ctrlObj)
        local attr = ctrlObj:GetAttribute()
        
        local function CalculateItemPos(dataIndex)
            local containerObj = ctrlObj:GetControlObject("container")
            
            local left = math.floor((dataIndex-1) % attr.ColumnNum) * (attr.ItemWidth + attr.ColumnSpace)
            local top = math.floor((dataIndex-1) / attr.ColumnNum) * (attr.ItemHeight + attr.RowSpace) + attr.VirtualTop
            local right = left + attr.ItemWidth
            local bottom = top + attr.ItemHeight
            
            return left, top, right, bottom
        end
        
        for dataInfo, itemId in pairs(attr.DataToItemMap) do
            local itemObj = ctrlObj:GetControlObject(itemId)
            local dataIndex = attr.ItemToIdexMap[itemId]
            local left, top, right, bottom = CalculateItemPos(dataIndex)
            local l,t,r,b = itemObj:GetObjPos()
            if left ~= l or top ~= t or right ~= r or bottom ~= b then
                itemObj:SetObjPos(left, top, right, bottom)
            end
        end
    end
    
    function AdjustVScrollBarPos(ctrlObj)
        local attr = ctrlObj:GetAttribute()
        local vsbarObj = ctrlObj:GetControlObject("vscrollbar.slider")
        local vsbarBkgObj = ctrlObj:GetControlObject("vscrollbar.bkg")
        local containerObj = ctrlObj:GetControlObject("container")
        
        local listTop = attr.VirtualTop
        local listBottom = attr.VirtualTop + math.ceil(#attr.IdexToDataMap / attr.ColumnNum) * (attr.ItemHeight+attr.RowSpace)
        local listHeight = listBottom - listTop
        local l,t,r,b = containerObj:GetObjPos()
        local containerHeight = b-t
        local l,t,r,b = vsbarBkgObj:GetObjPos()
        local vsbarBkgHeight = b-t
        
        -- 大小
        local vsbHeight = 0
        if listHeight > containerHeight then
            vsbHeight = vsbarBkgHeight * containerHeight / listHeight
        end
        
        -- 位置
        local vsbTop = 0
        if listHeight > containerHeight then
            vsbTop = vsbarBkgHeight * (-listTop) / listHeight
        end
        
        local l,t,r,b = vsbarObj:GetObjPos()
        if t ~= vsbTop or b-t ~= vsbHeight then
            vsbarObj:SetObjPos(l, vsbTop, r, vsbTop + vsbHeight)
        end
        
        -- 滚动调消失时, 背景也要消失
        if vsbHeight == 0 then
            vsbarBkgObj:SetVisible(false)
        else
            vsbarBkgObj:SetVisible(true)
        end
    end
    
    function CreateItemObj(ctrlObj)
        local attr = ctrlObj:GetAttribute()
        local containerObj = ctrlObj:GetControlObject("container")
        local factory = XLGetObject("Xunlei.UIEngine.ObjectFactory")
        
        -- 回收站里有 itemObj, 直接返回
        if #attr.RecycleItemList > 0 then
            local itemObj = attr.RecycleItemList[#attr.RecycleItemList]
            table.remove(attr.RecycleItemList)
            return itemObj
        end
        
        -- 分配 itemId
        local itemId = nil
        if #attr.ItemIdCache > 0 then
            itemId = attr.ItemIdCache[#attr.ItemIdCache]
            table.remove(attr.ItemIdCache)
        else
            itemId = "item" .. attr.ItemIdMax
            attr.ItemIdMax = attr.ItemIdMax + 1
        end
        
        -- 创建 itemObj
        local itemObj = factory:CreateUIObject(itemId, attr.ItemClass)
        containerObj:AddChild(itemObj)
        
        -- 设置监听器
        for eventName, _ in pairs(attr.ItemCookieMap) do
            local itemCookie = itemObj:AttachListener(eventName, true, function(...)
                OnItemEvent(eventName, select(1, ...))
            end)
            
            -- 把 itemObj 和 cookie 的关系记到表里面
            attr.ItemCookieMap[eventName][itemObj] = itemCookie
        end
        
        return itemObj
    end
    
    function RecycleItemObj(ctrlObj, itemId)
        local attr = ctrlObj:GetAttribute()
        local itemObj = ctrlObj:GetControlObject(itemId)
        
        if itemObj == nil then
            return
        end
        table.insert(attr.RecycleItemList, itemObj)
    end
    
    function CleanItemObj(ctrlObj)
        local attr = ctrlObj:GetAttribute()
        local containerObj = ctrlObj:GetControlObject("container")
        
        for i,itemObj in ipairs(attr.RecycleItemList) do
            -- itemId 用不着了,回收到 ItemIdCache 里下次使用
            local itemId = itemObj:GetID()
            table.insert(attr.ItemIdCache, itemId)
            
            if attr.ItemCookieMap[eventName] then
                attr.ItemCookieMap[eventName][itemObj] = nil
            end
            
            containerObj:RemoveChild(itemObj)
        end
        
        attr.RecycleItemList = {}
    end
    
    function SetItemData(ctrlObj, itemId, data)
        local attr = ctrlObj:GetAttribute()
        local itemObj = ctrlObj:GetControlObject(itemId)
        
        if itemObj == nil then
            return
        end
        itemObj[attr.ItemSetDataFunc](itemObj, data)
    end
    
    -- itemObj 发出事件时的回调函数
    function OnItemEvent(eventName, ...)
        local itemObj = select(1, ...)
        local ctrlObj = itemObj:GetOwnerControl()
        local attr = ctrlObj:GetAttribute()
        local itemId = itemObj:GetID()
        local dataIndex = attr.ItemToIdexMap[itemId]
        
        if attr.EventCookieMap[eventName] == nil then
            return
        end
        
        for cookie, callback in pairs(attr.EventCookieMap[eventName]) do
            callback(ctrlObj, dataIndex, select(1, ...))
        end
    end
  • 相关阅读:
    迷宫 填充法新思路(填充干扰路径)
    类模板使用说明
    thinkphp5项目--企业单车网站(二)
    thinkphp5项目--企业单车网站(一)
    thinkphp5项目--个人博客(八)
    PHP 二维数组去掉重复值并保持原结构
    PHP join() 函数
    PHP array_merge() 函数
    thinkphp5项目--个人博客(七)
    PHP str_replace() 和str_ireplace()函数
  • 原文地址:https://www.cnblogs.com/zuibunan/p/4511323.html
Copyright © 2020-2023  润新知