Cocos2dx-lua ScrollView[三]高级篇

news2024/12/23 4:39:22

一.概述

本文缩写说明:sv = ScrollView, cell代表ScrollView的一个子节点

本文介绍sv的一种封装类库,来实现快速创建sv,有如下几个优点:

1.item的位置通过参数控制,提高开发效率

2.免去了调用sv的API,提高开发效率

3.分帧创建,提高性能

4.可通过参数控制,复用item类似tableview,提高性能

本文和上一篇Cocos2dx-lua ScrollView[二]进阶篇-CSDN博客

对比有一定相似之处,但也有较多不同,读者可仔细对比代码实现,详细品读,有所取舍和偏爱

二.效果演示

三.代码实现

3.1 说明

a.下面2个模块需要require

b.svCmn是比较复杂的,有必要阅读代码掌握运行原理

c.代码原封不动搬到工程里基本可以正常运行(当然哪里出了问题读者得会排查,本文基本喂饭喂到嘴里了)

d.svCmn经过上线项目验证,可放心使用,在项目中大量推广

3.2 辅助定时器模块:GlobalTimeTicket

GlobalTimeTicket = GlobalTimeTicket or {}
 
auto_id = auto_id or 0
function autoId()
    auto_id = auto_id + 1
    return auto_id
end
-- 获取单例
-- New和不New只是一层一层调用__init和__delete,对于单例没有影响
function GlobalTimeTicket:getInstance()
    if not self.is_init then 
        self.scheduler = cc.Director:getInstance():getScheduler()
        self.schedulers = {}
        self.is_init = true
        self.is_stop = nil
    end
    return self
end
 
-- 定时回调 通用版
-- call_back : function     回调函数    必填
-- interval  : int          时间间隔    默认1 秒
-- limit_time: int          限制次数    默认0 无限
-- with_name : any          定时器标识  默认自增id
-- 返回用于删除的标识
-- simple    : local id = GlobalTimeTicket:getInstance():add(fun) ; GlobalTimeTicket:getInstance():remove(id)
--           : GlobalTimeTicket:getInstance():add(fun, 0.1, 1)              -- 次数达到自动删除
--           : GlobalTimeTicket:getInstance():add(fun, 0.1, 3, "name")      
function GlobalTimeTicket:add(call_back, interval, limit_time, with_name)
    if self.is_stop then return end
    with_name = with_name or autoId()
    if nil == call_back or self.schedulers == nil or nil ~= self.schedulers[with_name] then return end    -- 已经有定义了,不能重复
 
    limit_time = limit_time or 0
    interval = interval or 1
    local schedul_hander = self.scheduler:scheduleScriptFunc(function(dt)
        if self.is_stop then return end
        if call_back ~= nil then
            if limit_time == 1 then 
                self:remove(with_name)
            elseif limit_time > 1 then 
                limit_time = limit_time - 1
            end
            call_back(dt)
        end
    end, interval, false)
    self.schedulers[with_name] = schedul_hander
    return with_name
end
 
-- 删除一个定时器
function GlobalTimeTicket:remove(with_name)
    if with_name == nil then return end
    local schedul_hander = self.schedulers[with_name] 
    if schedul_hander ~= nil then
        self.scheduler:unscheduleScriptEntry(schedul_hander)
        self.schedulers[with_name] = nil 
    end
end
 
-- 清除所有定时器
function GlobalTimeTicket:removeAll()
    for _, v in pairs(self.schedulers) do 
        self.scheduler:unscheduleScriptEntry(v)
    end
    self.schedulers = {}
end
 
function GlobalTimeTicket:hasTicket(with_name)
    local schedul_hander = self.schedulers[with_name] 
    if schedul_hander ~= nil then
        return true
    end
    return false
end
 
function GlobalTimeTicket:getSchedulers()
    return self.schedulers
end
 
-- 停止定时器
function GlobalTimeTicket:stop()
    self.is_stop = true
    self:removeAll()
end

3.3 sv封装模块:svCmn

--[[使用例子
    if not self.svCmn then
        local setting = {
            start_x = 18, space_x = 0,
            start_y = 26, space_y = 6,
            item_width = 686, item_height = 172,
            row = 1, col = 1,
            delay = 4, once_num = 1,
        }
        self.svCmn = svCmn.new(self.scr_con, cc.p(0,0) , ScrollViewDir.vertical, ScrollViewStartPos.top, self.scr_con:getContentSize(), setting, cc.p(0, 0))
        self.svCmn:registerScriptHandlerSingle(handler(self,self.createNewCell), ScrollViewFuncType.CreateNewCell)
        self.svCmn:registerScriptHandlerSingle(handler(self,self.numberOfCells), ScrollViewFuncType.NumberOfCells)
        self.svCmn:registerScriptHandlerSingle(handler(self,self.updateCellByIndex), ScrollViewFuncType.UpdateCellByIndex)
    end
    self.svCmn:reloadData()
]]--

--ScrollView的方法类型
ScrollViewFuncType = {
    UpdateCellByIndex = 1,      -- 更新cell体
    CreateNewCell  =  2,        -- 创建 新的cell 
    NumberOfCells = 3,          -- 返回 数据的数量
    OnCellTouched = 4,          -- 点击cell回调方法
}

svCmn = class("svCmn", function()
    return ccui.Layout:create()
end)

function svCmn:ctor(parent, pos, dir, start_pos, size, setting, ap)
    self.parent = parent
    self.pos = pos or cc.p(0, 0)
    self.dir = dir or ScrollViewDir.vertical
    self.start_pos = start_pos or ScrollViewStartPos.top
    self.size = size or cc.size(100, 100)
    self.ap = ap or cc.p(0, 0)

    self.allCellList = {}       --存放cell对象和其坐标,结构:{x, y, cell}, cell存在重复对象, 长度=cell总数量
    self.cacheList = {}         --保存所有实际创建的cell, 长度=cell最大显示数量
    self.activeCellIdx = {}     --保存每个位置的cell当前是否处于显示状态, 长度=cell总数量
    self.handler = {}           --回调方法
    self.time_show_index = 0    --到时间显示的索引
    self.is_first_init = true   --是否初始化

    self:analysisSetting(setting)
    self:createRootWnd()
end

--要求规定setting的所有变量 都应该在这里定义出来
function svCmn:analysisSetting(setting)
    self.setting        = setting or {}
    self.start_x        = self.setting.start_x or 0             -- 第一个单元的起点X
    self.end_x          = self.setting.end_x or self.start_x    -- 最后一个单元结束X间隔 如果是nil 默认 和 start_x一致
    self.start_y        = self.setting.start_y or 0             -- 第一个单元的起点Y
    self.end_y          = self.setting.end_y or self.start_y    -- 最后一个单元结束Y间隔 如果是nil 默认 和 start_y一致
    self.space_x        = self.setting.space_x or 3             -- 横向间隔空间
    self.space_y        = self.setting.space_y or 3             -- 竖向间隔空间
    self.item_width     = self.setting.item_width or 115        -- 单元的宽度
    self.item_height    = self.setting.item_height or 115       -- 单元的高度

    self.row            = self.setting.row or 5                 -- 行数,作用于水平方向的滚动
    self.col            = self.setting.col or 5                 -- 列数,作用于垂直方向的滚动
    self.delay          = 1 --self.setting.delay or 4           -- 创建延迟时间 强制改为1 
    self.once_num       = self.setting.once_num or 1            -- 每次创建的数量
    self.need_dynamic   = true  -- 默认是无限的
    self.checkovercallback = self.setting.checkovercallback     --滑动回调函数
    self.is_auto_scroll = setting.is_auto_scroll or false       --是否自动判断是否能滚动..个数小于一屏大小时候scroll 不能滚动

    --位置列表 
    self.position_data_list = self.setting.position_data_list

    --固定容器大小 如果有值.将不运算容器大小
    self.container_width = setting.container_width
    self.container_height = setting.container_height

    self.inner_hight_offset = setting.inner_hight_offset or 0 -- 内容高度偏移值(仅对纵向有效)

    --横向的只支持一行的..
    if self.dir == ScrollViewDir.horizontal then
        self.row  = 1
    end
    self:calculationMaxSum()
end

function svCmn:updateSetting(setting)
    if not setting then return end
    for k,v in pairs(setting) do
        self[k] = v
    end
end

--desc:计算一下一屏最多创建的个数
function svCmn:calculationMaxSum()
    local max_sum
    if self.dir == ScrollViewDir.horizontal then 
        max_sum = (math.ceil(self.size.width / (self.item_width + self.space_x)) + 1) * self.row
    else
        max_sum = (math.ceil(self.size.height / (self.item_height + self.space_y)) + 1) * self.col
    end
    self.cacheMaxSize = max_sum
end

function svCmn:createRootWnd()
    self:setContentSize(self.size)
    if not tolua.isnull(self.parent) then
        self.parent:addChild(self)
    end
    self:setPosition(self.pos)
    self:setAnchorPoint(self.ap)
    
    self.scroll_view = createScrollView(self.size.width, self.size.height, 0, 0, self, self.dir) 
    self.container = self.scroll_view:getInnerContainer() 
    self:registerEvent()
end

function svCmn:registerEvent()
    if self.need_dynamic == true then
        self.scroll_view:addEventListener(function(sender, eventType)
            if eventType == ccui.ScrollviewEventType.containerMoved then
                self:checkRectIntersectsRect()
                if self.checkovercallback then
                    self.checkovercallback()
                end
            end
        end)
    end
end

function svCmn:registerScriptHandlerSingle(func, handlerId)
    self.handler[handlerId] = func
end

function svCmn:numberOfCells()
    local cells = ScrollViewFuncType.NumberOfCells
    if not self.handler or not self.handler[cells] then return end
    return self.handler[cells]()
end

--刷新每一个cell 
function svCmn:updateCellByIndex(cell, index)
    if not self.handler[ScrollViewFuncType.UpdateCellByIndex] then return end
    if not cell.index then
        cell.create_index = index
    end
    print("item刷新ing", "item创建时的索引:"..cell.create_index, "item数据索引变化:" .. (cell.index or "无") .. " -> " .. index)
    self.handler[ScrollViewFuncType.UpdateCellByIndex](cell, index)
end

--创建一个新cell
function svCmn:createNewCell(idx)
    if not self.handler[ScrollViewFuncType.CreateNewCell] then return end
    print("createNewCell", idx)
    return self.handler[ScrollViewFuncType.CreateNewCell](self.item_width, self.item_height, idx)
end

-- --点击cell --在createNewCell 自行实现
function svCmn:onCellTouched(cell, index)
    if not self.handler[ScrollViewFuncType.OnCellTouched] then return end
    self.handler[ScrollViewFuncType.OnCellTouched](cell, index)
end


--设置 scrollview 是否可点
function svCmn:setClickEnabled(status)
    self.scroll_view:setTouchEnabled(status)
end
--设置 是否吞噬点击
function svCmn:setSwallowTouches(status)
    self.scroll_view:setSwallowTouches(status)
end

function svCmn:setBounceEnabled( status )
    self.scroll_view:setBounceEnabled(status)
end

--desc:移动的过程中盘点是否不再可视范围,不再的时候移除掉,放到对象池,并且准备下一次创建
function svCmn:checkRectIntersectsRect()
    if self.dir == ScrollViewDir.vertical then
        if self.start_pos == ScrollViewStartPos.top then
            self:checkOverShowByVertical()
        else
            -- 支持ScrollViewStartPos.bottom的了 --by lwc
            self:checkOverShowByVerticalBottom()
        end
    elseif self.dir == ScrollViewDir.horizontal then
        self:checkOverShowByHorizontal()
    end
end

function svCmn:checkOverShowByVertical()
    if not self.allCellList then return end
    local container_y = self.container:getPositionY()
    --计算 视图的上部分和下部分在self.container 的位置
    local bot = -container_y
    local top = self.size.height + bot
    local col_count = math.ceil(#self.allCellList/self.col)
    --下面因为 self.allCellList 是一维数组 所以要换成二维来算
    --活跃cell开始行数
    local activeCellStartRow = 1
    for i=1, col_count do
        local index = 1 + (i-1)* self.col
        local cell = self.allCellList[index]
        activeCellStartRow = i
        if cell and cell.y - self.item_height * 0.5 <= top then
            break
        end
    end
    --活跃cell结束行数
    local activeCellEndRow = col_count
    if bot > 0 then
        for i = activeCellStartRow, col_count do
            local index = 1 + (i-1)* self.col
            local cell = self.allCellList[index]
            if cell and cell.y + self.item_height * 0.5 < bot then
                activeCellEndRow = i - 1
                break
            end
        end
    end
    -- print("@保留--> top --> :"..top .." self.col:"..self.col)
    -- print("@保留--> bot --> :"..bot )
    -- print("@保留--> 开始行: "..activeCellStartRow.."@结束行: "..activeCellEndRow)
    local max_count = self:numberOfCells()
    if max_count then
        for i=1, col_count do
            if i >= activeCellStartRow and i <= activeCellEndRow then
                for k=1, self.col do
                    local index = (i-1) * self.col + k
                    if not self.activeCellIdx[index] then
                        if index <= max_count then
                            self:updateCellAtIndex(index)
                            self.activeCellIdx[index] = true
                        end
                    end    
                end
            else
                for k=1, self.col do
                    local index = (i-1) * self.col + k
                    if index <= max_count then
                        self.activeCellIdx[index] = false
                    end
                end
            end
        end
    end
end

function svCmn:checkOverShowByVerticalBottom()
    if not self.allCellList then return end
    local container_y = self.container:getPositionY()
    --计算 视图的上部分和下部分在self.container 的位置
    local bot = -container_y
    local top = self.size.height + bot
    local col_count = math.ceil(#self.allCellList/self.col)
    --下面因为 self.allCellList 是一维数组 所以要换成二维来算
    --活跃cell开始行数
    local activeCellStartRow = col_count
    for i=col_count, 1,-1 do
        local index = 1 + (i-1)* self.col
        local cell = self.allCellList[index]
        activeCellStartRow = i
        if cell and cell.y - self.item_height * 0.5 <= top then
            break
        end
    end
    --活跃cell结束行数
    local activeCellEndRow = 1
    if bot > 0 then
        for i = activeCellStartRow, 1, -1 do
            local index = 1 + (i-1)* self.col
            local cell = self.allCellList[index]
            if cell and cell.y + self.item_height * 0.5 < bot then
                activeCellEndRow = i + 1
                break
            end
        end
    end
    -- print("@保留--> top --> :"..top .." self.col:"..self.col)
    -- print("@保留--> bot --> :"..bot )
    -- print("@保留--> 开始行: "..activeCellStartRow.."@结束行: "..activeCellEndRow)
    local max_count = self:numberOfCells()
    for i=1, col_count do
        if i <= activeCellStartRow and i >= activeCellEndRow then
            for k=1, self.col do
                local index = (i-1) * self.col + k
                if not self.activeCellIdx[index] then
                    if index <= max_count then
                        self:updateCellAtIndex(index)
                        self.activeCellIdx[index] = true
                    end
                end    
            end
        else
            for k=1, self.col do
                local index = (i-1) * self.col + k
                if index <= max_count then
                    self.activeCellIdx[index] = false
                end
            end
        end
    end
end

function svCmn:checkOverShowByHorizontal()
    if not self.allCellList then return end
    
    local container_x = self.container:getPositionX()
    --计算 视图的左部分和右部分在self.container 的位置
    local top = -container_x 
    local bot = top + self.size.width

    local row_count = #self.allCellList
    --横向的只支持一行
    --活跃cell开始行数
    local activeCellStartRow = 1
    if top > 0 then
        for index=1, row_count do
            local cell = self.allCellList[index]
            activeCellStartRow = index
            if cell and cell.x + self.item_width * 0.5 >= top then
                break
            end
        end
    end
    --活跃cell结束行数
    local activeCellEndRow = row_count
    for index = activeCellStartRow, row_count do
        local cell = self.allCellList[index]
        if cell and cell.x - self.item_width * 0.5 > bot then
            activeCellEndRow = index - 1
            break
        end
    end
    -- print("@保留--> top --> :"..top .." self.row:"..self.row)
    -- print("@保留--> bot --> :"..bot )
    -- print("@保留--> 开始行: "..activeCellStartRow.."@结束行: "..activeCellEndRow)
    local max_count = self:numberOfCells()
    if max_count then
        for index=1, row_count do
            if index >= activeCellStartRow and index <= activeCellEndRow then
                if not self.activeCellIdx[index] then
                    if index <= max_count then
                        self:updateCellAtIndex(index)
                        self.activeCellIdx[index] = true
                    end
                end 
            else
                if index <= max_count then
                    self.activeCellIdx[index] = false
                end
            end
        end
    end
end

--desc:滚动容器移动到指定位置
function svCmn:updateMove(pos)
    local target_pos = self:checkPosition(pos.x, pos.y)
    local move_to = cc.MoveTo:create(0.1, cc.p(target_pos.x, target_pos.y))
    local ease_out = cc.EaseSineOut:create(move_to)
    self.container:runAction(cc.Sequence:create(ease_out))
end

function svCmn:jumpToMove(pos, time, callback)
    local target_pos = self:checkPosition(pos.x, pos.y)
    time = time or 1
    local move_to = cc.MoveTo:create(time, cc.p(target_pos.x, target_pos.y))
    self.container:runAction(cc.Sequence:create(move_to, cc.CallFunc:create(function()
        if callback then
            callback()
        end
    end)))
end 

function svCmn:checkPosition(x, y)
    local _x, _y = self.container:getPositionX(), self.container:getPositionY()
    if self.dir == ScrollViewDir.horizontal then
        _x = _x + x
    elseif self.dir == ScrollViewDir.vertical then
        _y = _y + y
    end
    if _x > 0 then
        _x = 0
    elseif _x < (self.size.width - self.container_size.width) then
        _x = self.size.width - self.container_size.width
    end

    if _y > 0 then
        _y = 0
    elseif _y < (self.size.height - self.container_size.height) then
        _y = self.size.height - self.container_size.height
    end
    return cc.p(_x, _y)
end

--获取当前容器的y位置
function svCmn:getCurContainerPosY()
    if self.container and not tolua.isnull(self.container) then
        return self.container:getPositionY()
    end
end
--获取当前容器的x位置
function svCmn:getCurContainerPosX()
    if self.container and not tolua.isnull(self.container) then
        return self.container:getPositionX()
    end
end

function svCmn:setInnerContainer()
    local number = self:numberOfCells()
    local container_width = self.container_width or self.size.width
    local container_height = self.container_height or self.size.height
    if self.dir == ScrollViewDir.horizontal then  -- 水平
        if self.container_width == nil then
            local num = math.ceil(number / self.row)
            container_width = num * self.item_width + self.end_x + self.start_x + (num - 1) * self.space_x
        end
    else
        if self.container_height == nil then
            local num = math.ceil(number / self.col)
            container_height = num * self.item_height + self.end_y + self.start_y + (num - 1) * self.space_y + self.inner_hight_offset
        end
    end
    container_width = math.max(container_width, self.size.width)
    container_height = math.max(container_height, self.size.height)
    self.container_size = cc.size(container_width, container_height)
    --记录在当前的contariner位置..因为在 setInnerContainerSize 方法会被重置
    self.cur_container_x, self.cur_container_y = self.container:getPosition()
    
    self.scroll_view:setInnerContainerSize(self.container_size)
    if self.start_pos == ScrollViewStartPos.top then
        self.scroll_view:jumpToTop()
    elseif self.start_pos == ScrollViewStartPos.bottom then
        self.scroll_view:jumpToBottom()
    end
end

--刷新当前显示的item数据 (不改变任何位置的,前提数据数量没有改变如果有改变用 reload)
function svCmn:resetCurrentItems()
    for i,v in pairs(self.activeCellIdx) do
        if v then
            self:updateCellAtIndex(i)
        end
    end
end

--根据index 刷新对应索引..如果在显示视图内
function svCmn:resetItemByIndex(index)
    -- body
    if self.activeCellIdx[index] then
        self:updateCellAtIndex(index)
    end
end

--获取活跃中的cell对象
function svCmn:getActiveCellList()
    local list = {}
    for i,v in pairs(self.activeCellIdx) do
        if v and self.allCellList[i] and self.allCellList[i].cell then
            table.insert(list, self.allCellList[i].cell)
        end
    end
    return list
end

--获取index索引对应cell(不管是否活跃)
function svCmn:getCellByIndex(index)
    if  self.allCellList[index] and self.allCellList[index].cell then
        return self.allCellList[index].cell
    end
end

--获取index索引对应cellXY位置(不管是否活跃)
function svCmn:getCellXYByIndex(index)
    if  self.allCellList[index] then
        return self.allCellList[index].x, self.allCellList[index].y 
    end
end

--获取index索引对应cellXY位置(不活跃会返回空)
function svCmn:getActiveCellByIndex(index)
    if self.activeCellIdx[index] and self.allCellList[index] then
        return self.allCellList[index].cell
    end
end
--获取当前容器所在显示窗口的x y位置
function svCmn:getContainerXY()
    if self.container then
        local x, y = self.container:getPosition()
        return x, y
    end
end

--获取当前容器所在显示窗口的x y位置
function svCmn:setContainerXY(x, y)
    if self.container then
        if x and y then
            self.container:setPosition(x,y)
        else
            if x then
                self.container:setPositionX(x) 
            end
            if y then 
                self.container:setPositionY(y)  
            end    
        end
    end
end

--根据索引判断是否活跃中
function svCmn:isActiveByIndex(index)
    if self.activeCellIdx[index] then
        return true
    end
    return false
end
--移动到以选中idenx的位置作为在中间 显示 目前只支持y 方向的
function svCmn:jumpToMoveByIndex(index)

    if not self.allCellList[index] then return end
    local y = self.allCellList[index].y or 0
    local pos = self.container_size.height - (y + self.size.height * 0.5 )
    if pos < 0 then
        pos = 0
    end
    local pos_per = pos * 100 / (self.container_size.height - self.size.height)
    if pos_per ~= pos_per then
        pos_per = 0;
    end
    if pos_per > 100 then
        pos_per = 100
    end
    if pos_per == 100 then
        if self.start_pos == ScrollViewStartPos.top then
            self:checkOverShowByVertical()
        else
            self:checkOverShowByVerticalBottom()
        end
    end
    self.scroll_view:scrollToPercentVertical(pos_per, 0.8, true)
end

--desc:设置数据
--select_idnex 从第几个开始
--@setting: 如果有改变的话
--@is_keep_position 是否保持原来位置 --item数量有变化情况. 无变化请用resetCurrentItems
function svCmn:reloadData(select_index, setting, is_keep_position)
    if setting then
        self:updateSetting(setting)
    end
    local old_width , old_height = 0, 0
    if self.container_size then
        old_width = self.container_size.width
        old_height = self.container_size.height
    end
    self.allCellList = {}
    self.activeCellIdx = {}

    for k, v in ipairs(self.cacheList) do
        --相当于隐藏
        v:setPositionX(-10000)
    end
    --设置容器大小
    self:setInnerContainer()

    local number = self:numberOfCells()
    if number == 0 then
        return
    end

    for i = 1, number do
        local cell = nil 
        if i <= self.time_show_index then
            cell = self:getCacheCellByIndex(i)
        end
        local count = #self.allCellList
        local x, y
        if self.position_data_list then
            local pos = self.position_data_list[count + 1]
            if pos then
                x, y = pos.x, pos.y
            else
                x, y = self:getCellPosition(count + 1)    
            end
        else
            x, y = self:getCellPosition(count + 1)
        end
        local cellData = {cell = cell, x = x, y = y}
        table.insert(self.allCellList, cellData)
    end
    
    if self.is_first_init then
        self:startTimeTicket()
    else
        --如果时间显示索引小于总数 应该显示继续当前定时器 让下面的能显示出来
        if self.time_show_index <= number then
            self:startTimeTicket()
        end
    end

    if is_keep_position then
        --是否保持当前显示位置
        local cur_container_x =  self.cur_container_x or 0
        local cur_container_y =  self.cur_container_y or 0
        if self.dir == ScrollViewDir.vertical then --竖方向
            if self.start_pos == ScrollViewStartPos.top then
                local temp_height = self.container_size.height - old_height
                cur_container_y = cur_container_y -  temp_height
            end
            if cur_container_y > 0 then
                cur_container_y = 0
            elseif cur_container_y < (self.size.height - self.container_size.height) then
                cur_container_y = self.size.height - self.container_size.height
            end
        elseif self.dir == ScrollViewDir.horizontal then --横方向
            if cur_container_x > 0 then
                cur_container_x = 0
            elseif cur_container_x < (self.size.width - self.container_size.width) then
                cur_container_x = self.size.width - self.container_size.width
            end
        end
        self.container:setPosition(cur_container_x, cur_container_y)
        self:checkRectIntersectsRect()
    else
        if select_index == nil then
            local maxRefreshNum 
            if self.dir == ScrollViewDir.horizontal then  -- 水平
                maxRefreshNum = self.cacheMaxSize - self.row
            else
                maxRefreshNum = self.cacheMaxSize - self.col
            end
            local refreshNum = number < maxRefreshNum and number or maxRefreshNum

            for i = 1, refreshNum do
                if i <= self.time_show_index then
                    self:updateCellAtIndex(i)
                end
                self.activeCellIdx[i] = true
            end
        else
            self:selectCellByIndex(select_index)
        end
    end
    if self.is_auto_scroll then
        local cur_max_count = self.cacheMaxSize
        
        if self.dir == ScrollViewDir.horizontal then 
            cur_max_count = cur_max_count - 2 * self.row
        else
            cur_max_count = cur_max_count - 2 * self.col
        end
        if number <= cur_max_count then
            self:setClickEnabled(false)
        else
            self:setClickEnabled(true)
        end
    end
end

--选中index索引对象(如果列表允许 会排序在开始第一位)
function svCmn:selectCellByIndex(index)
    local index = index or 1
    if self.allCellList[index] == nil then
        index = 1
    end
    if self.allCellList[index] == nil then  return end
    --一屏幕显示的最大数量
    local maxRefreshNum 
    if self.dir == ScrollViewDir.horizontal then  -- 水平
        maxRefreshNum = self.cacheMaxSize - self.row
    else
        maxRefreshNum = self.cacheMaxSize - self.col
    end
    local number = self:numberOfCells()
    if number < maxRefreshNum then
        --不够显示一屏幕
        if self.time_show_index == 0 then
            self.time_show_index = index
        end
        for i = 1, number do
            if i <= self.time_show_index then
                self:updateCellAtIndex(i)
            end
            self.activeCellIdx[i] = true
        end
    else
        --列表允许 情况
        if self.dir == ScrollViewDir.horizontal then  -- 水平
            --容器x方向位置
            local container_x
            if index == 1 then
                container_x =  0
            else
                container_x =  -(self.allCellList[index].x - (self.item_width + self.space_x) * 0.5 )
            end
            --容器x方向最大位置
            local max_contariner_x = -(self.container_size.width - self.size.width)

            --这两个值都是负数
            if container_x < max_contariner_x then
                container_x = max_contariner_x
            end
            local show_index = math.floor(math.abs(container_x) / self.item_width) + 1
            if self.time_show_index < show_index then
                self.time_show_index = show_index
            end
            self.container:setPositionX(container_x)
            self:checkRectIntersectsRect()
        else -- 垂直
            local container_y
            if index == 1 then
                container_y = (self.start_y + self.allCellList[index].y + self.item_height * 0.5) - self.size.height 
            else
                container_y = (self.allCellList[index].y + (self.item_height + self.space_y) * 0.5) - self.size.height 
            end
            if container_y < 0 then
                container_y = 0
            end
            local index_1 = math.floor( (self.container_size.height - (container_y + self.size.height)) / self.item_height) + 1
            local show_index = (index_1 - 1) * self.col + 1
            if self.time_show_index < show_index then
                self.time_show_index = show_index
            end
            self.container:setPositionY(- container_y)
            self:checkRectIntersectsRect()
        end
    end

    if index > 0 and index <= self:numberOfCells() then
        local cell = self:getCacheCellByIndex(index)
        cell.index = index
        self.allCellList[index].cell = cell
        self:onCellTouched(cell, index)
    end 
end

function svCmn:setOnCellTouched(index)
    local cell = self:getCacheCellByIndex(index)
    cell.index = index
    self.allCellList[index].cell = cell
    self:onCellTouched(cell, index)
end

function svCmn:startTimeTicket()
     if self.time_ticket == nil then
        if #self.allCellList == 0 then
            return
        end
        --到时间显示的索引
        local once_num = self.once_num or 1
        local _callback = function()
            if tolua.isnull(self.container) then return end
            local count = self.time_show_index + once_num
            local index = self.time_show_index + 1
            if index == 0 then
                index = 1
            end
            local size = #self.allCellList

            self.time_show_index = self.time_show_index + once_num
            for i = index, count do
                if i > size then
                    --超过总数了
                    break
                end
                local cellData = self.allCellList[i]
                if cellData and cellData.cell == nil then
                    cellData.cell = self:getCacheCellByIndex(i)
                end
                if self.activeCellIdx[i] then
                    self:updateCellAtIndex(i)
                end
            end
            
            if self.time_show_index >= size then
                self:clearTimeTicket()
                self.is_first_init = false
            end
        end

        self.time_ticket = GlobalTimeTicket:getInstance():add(_callback, self.delay / display.DEFAULT_FPS)
    end
end

function svCmn:clearTimeTicket()
    if self.time_ticket ~= nil then
        GlobalTimeTicket:getInstance():remove(self.time_ticket)
        self.time_ticket = nil
    end
end 

function svCmn:getCellPosition(index)
    local cur_item_index = index
    local anchor_point = cc.p(0.5,0.5)
    local _x, _y = 0, 0
    if self.dir == ScrollViewDir.horizontal then
        _x = self.start_x + self.item_width * anchor_point.x +(self.item_width + self.space_x) *(math.floor((index - 1) / self.row))
        _y = self.container_size.height -(self.start_y + self.item_height *(1 - anchor_point.y) +((index - 1) % self.row) *(self.item_height + self.space_y))
    else
        if self.start_pos == ScrollViewStartPos.top then
            _x = self.start_x + self.item_width * anchor_point.x + (self.item_width + self.space_x) *((index - 1) % self.col)
            _y = self.container_size.height -(self.start_y + self.item_height *(1 - anchor_point.y) +(math.floor((index - 1) / self.col)) *(self.item_height + self.space_y))
        else
            _x = self.start_x + self.item_width * anchor_point.x +(self.item_width + self.space_x) *((index - 1) % self.col)
            _y = self.start_y + self.item_height * anchor_point.y +(math.floor((index - 1) / self.col)) *(self.item_height + self.space_y)
        end
    end
    return _x, _y
end

--通过创建或复用的方式,获取index对应的cell对象
function svCmn:getCacheCellByIndex(index)
    local cacheIndex = (index - 1) % self.cacheMaxSize + 1
    if not self.cacheList[cacheIndex] then
        local newCell = self:createNewCell(index)
        if newCell then
            newCell:setAnchorPoint(cc.p(0.5, 0.5))
            newCell:setPositionX(-10000)--隐藏
            self.cacheList[cacheIndex] = newCell
            self.scroll_view:addChild(newCell)
        end
        return newCell
    else
        return self.cacheList[cacheIndex]
    end
end

--cell设置位置,并刷新cell的UI
function svCmn:updateCellAtIndex(index)
    if index > self.time_show_index then
        return
    end
    if not self.allCellList[index] then return end
    local cellData = self.allCellList[index]
    if cellData.cell == nil then
        cellData.cell = self:getCacheCellByIndex(index)     --self.allCellList的cell赋值在这里
    end
    cellData.cell:setPosition(cellData.x, cellData.y)
    self:updateCellByIndex(cellData.cell, index)
end

function svCmn:clearTimeTicket()
    if self.time_ticket ~= nil then
        GlobalTimeTicket:getInstance():remove(self.time_ticket)
        self.time_ticket = nil
    end
end 

function svCmn:getMaxSize()
    return self.container_size
end

function svCmn:getContainer()
    return self.container
end

function svCmn:scrollToPercentVertical( percent, time )
    if percent ~= percent then percent = 0 end
    self.scroll_view:scrollToPercentVertical(percent, time, true)
end

function svCmn:DeleteMe()
    doStopAllActions(self.container)
    self:clearTimeTicket()
    for k, item in ipairs(self.cacheList) do
        if item.DeleteMe then
            item:DeleteMe()
        end
    end
    self.allCellList = nil
    self.activeCellIdx = nil
    self.cacheList = nil
    self:removeAllChildren()
    self:removeFromParent()
end

3.4 应用举例

function ModuleTest:updateSVCmn()
    self.cellData = {
        [1] = {index = 1},
        [2] = {index = 2},
        [3] = {index = 3},
        [4] = {index = 4},
        [5] = {index = 5},
        [6] = {index = 6},
        [7] = {index = 7},
        [8] = {index = 8},
        [9] = {index = 9},
        [10] = {index = 10},
        [11] = {index = 11},
        [12] = {index = 12},
    }
    if not self.svCmn then
        local setting = {
            start_x = 18, space_x = 0,
            start_y = 26, space_y = 6,
            item_width = 686, item_height = 172,
            row = 1, col = 1,
            delay = 4, once_num = 1,
        }
        self.svCmn = svCmn.new(self.scr_con, cc.p(0,0) , ScrollViewDir.vertical, ScrollViewStartPos.top, self.scr_con:getContentSize(), setting, cc.p(0, 0))
        self.svCmn:registerScriptHandlerSingle(handler(self,self.createNewCell), ScrollViewFuncType.CreateNewCell)
        self.svCmn:registerScriptHandlerSingle(handler(self,self.numberOfCells), ScrollViewFuncType.NumberOfCells)
        self.svCmn:registerScriptHandlerSingle(handler(self,self.updateCellByIndex), ScrollViewFuncType.UpdateCellByIndex)
    end
    self.svCmn:reloadData()
end

function ModuleTest:createNewCell(width, height)
    local cell = ActionCommonItem.new()
	return cell
end

function ModuleTest:numberOfCells()
    if not self.cellData then return 0 end
    return #self.cellData
end

function ModuleTest:updateCellByIndex(cell, index)
    local onecellData = self.cellData[index]
    cell.root_wnd:getChildByName("label"):setString(onecellData.index)
end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1552490.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[flink] flink macm1pro 快速使用从零到一

文章目录 快速使用 快速使用 打开 https://flink.apache.org/downloads/ 下载 flink 因为书籍介绍的是 1.12版本的&#xff0c;为避免不必要的问题&#xff0c;下载相同版本 解压 tar -xzvf flink-1.11.2-bin-scala_2.11.tgz启动 flink ./bin/start-cluster.sh打开 flink web…

【JavaSE】String类详解

目录 前言 1. 什么是String类 1.1 String的构造 1.2 String类的基本操作&#xff1a;打印、拼接、求字符串长度 2. String类的常用方法 2.1 字符串查找 2.2 字符串替换 2.3 字符串拆分 2.4 字符串截取 2.5 字符串和其他类型的转换 2.6 去除字符串左右两边的空格 3.…

大模型 智能体 智能玩具 智能音箱 构建教程 wukong-robot

视频演示 10:27 一、背景 继上文《ChatGPT+小爱音响能擦出什么火花?》可以看出大伙对AI+硬件的结合十分感兴趣,但上文是针对市场智能音响的AI植入,底层是通过轮询拦截,算是hack兼容,虽然官方有提供开发者接口,也免不了有许多局限性(比如得通过特定指令唤醒),不利于我…

计算机网络——29ISP之间的路由选择:BGP

ISP之间的路由选择&#xff1a;BGP 层次路由 一个平面的路由 一个网络中的所有路由器的地位一样通过LS&#xff0c;DV&#xff0c;或者其他路由算法&#xff0c;所有路由器都要知道其他所有路由器&#xff08;子网&#xff09;如何走所有路由器在一个平面 平面路由的问题 …

JavaEE初阶Day 4:多线程(2)

目录 Day4&#xff1a;多线程&#xff08;2&#xff09;1. catch语句2. sleep的处理3. Thread3.1 Thread构造方法3.2 Thread的属性3.2.1 ID3.2.2 优先级3.2.3 后台线程3.2.4 存活3.2.5 start3.2.6 中断3.2.6.1 控制线程结束代码3.2.6.2 interrupt和isInterrupted Day4&#xff…

学习笔记——微信小程序读取当前时间

<view class"box"><text>日期:</text><view class"date">{{obtaindate}}</view></view> wxml中定义了一个文本元素&#xff0c;通过{{obtaindate}}获取js页面传递的日期数据 data:{obtaindate:"" }, onlo…

公链角逐中突围,Solana 何以成为 Web3 世界的流量焦点?

在众多区块链公链中&#xff0c;Solana 凭借其创纪录的处理速度和极低的交易费用&#xff0c;成为了众多开发者和投资者的宠儿。就像网络上流行的那句话所说&#xff1a;“Why slow, when you can Solana?”&#xff0c;Solana 正以它的速度和强大的生态系统&#xff0c;重新定…

nacos的各种类型的配置文件 yml 、json、 Properties、 text 等文件类型 发生变化怎么热更新,实现实时监听nacos配置文件变化

本文用的是 Nacos作为配置中心注册监听器方法 实现热更新 nacos 配置文件 从而不用重启项目 依赖、工具类 这边就不写了 因为项目用的是 Json 类型的配置文件 所以下文 主要是对json文件进行实现 别的文件大同小异 先说扯淡的东西 在nacos 的配置文件中 dataId 这两种声明 是…

Postman传对象失败解决

文章目录 情景复现解决方案总结 情景复现 postman中调用 debug发现pId传入失败 分析解释&#xff1a; 实体类中存在pId、uid和num字段 controller层将GoodsCar作为请求体传入 解决方案 当时觉得很奇怪&#xff0c;因为uid和num可以被接收&#xff0c;而pId和num的数据类型相…

图腾柱PFC:HP1010为您的电动两轮车之旅提供绿色,高效,安全的动力

电动两轮车不仅为当今生活提供了便利&#xff0c;更是一种健康和绿色的出行方式。想象一下&#xff0c;在经过一整晚的充分休息&#xff0c;骑上爱车&#xff0c;满血复活的准备开始新的一天。您会愿意带着如何给心爱的两轮车充电的担心开始这一天吗&#xff1f; 随着越来越…

HackTheBox-Machines--Legacy

文章目录 1 端口扫描2 测试思路3 445端口漏洞测试4 flag Legacy 测试过程 1 端口扫描 nmap -sC -sV 10.129.227.1812 测试思路 目标开启了135、139、445端口&#xff0c;445 SMB服务存在很多可利用漏洞&#xff0c;所以测试点先从445端口开始。而且在Nmap扫描结果中&#xff0c…

Unity 窗口化设置

在Unity中要实现窗口化&#xff0c;具体设置如下&#xff1a; 在编辑器中&#xff0c;选择File -> Build Settings。在Player Settings中&#xff0c;找到Resolution and Presentation部分。取消勾选"Fullscreen Mode"&#xff0c;并选择"Windowed"。设…

Unity2018发布安卓报错 Exception: Gradle install not valid

Unity2018发布安卓报错 Exception: Gradle install not valid Exception: Gradle install not valid UnityEditor.Android.GradleWrapper.Run (System.String workingdir, System.String task, System.Action1[T] progress) (at <c67d1645d7ce4b76823a39080b82c1d1>:0) …

通用指南-营销和设计中的增强现实(AR)

原文作者&#xff1a;Superside 翻译&#xff1a;数字化营销工兵 --- 经典万字长文&#xff0c;权威解读&#xff0c;分享经典&#xff0c;预计阅读完需要30分钟&#xff0c;建议收藏&#xff01; 目录 一、引言 为什么要尝试AR AR到底是什么&#xff1f;营销人员和创意人…

网络工程师实验命令(华为数通HCIA)

VRP系统的基本操作 dis version #查看设备版本信息 sys #进入系统视图 system-name R1 #改设备名字为R1进入接口配置IP地址 int g0/0/0 ip address 192.168.1.1 255.255.255.0 #配置接口地址为192.168.1.1/255.255.255.0 ip address 192.168.1.2 24 sub #此…

Intellij IDEA安装配置Spark与运行

目录 Scala配置教程 配置Spark运行环境 编写Spark程序 1、包和导入 2、定义对象 3、主函数 4、创建Spark配置和上下文 5、定义输入文件路径 6、单词计数逻辑 7、输出结果 8、完整代码&#xff1a; Scala配置教程 IDEA配置Scala&#xff1a;教程 配置Spark运行环境 …

持续集成流程主要系统构成介绍(CI)

目录 一、概述 二、版本控制系统 2.1 概述 2.2 版本控制系统使用流程示意图 2.3 版本控制软件划分 2.3.1 集中式版本控制软件 2.3.2 分布式版本控制软件 2.3.3 总结 2.4 常用版本控制软件介绍 三、编译构建系统 3.1 概述 3.2 编译构建流程示意图 3.3 列举Java 源码…

uniApp使用XR-Frame创建3D场景(5)材质贴图的运用

上一篇讲解了如何在uniApp中创建xr-frame子组件并创建简单的3D场景。 这篇我们讲解在xr-frame中如何给几何体赋予贴图材质。 先看源码 <xr-scene render-system"alpha:true" bind:ready"handleReady"><xr-node><xr-assets><xr-asse…

向量法求点在直线上的投影

已知直线上两点a、b和直线外一点p&#xff0c;求p在直线ab上的投影点。 根据《计算几何之 点在直线上的投影 代码模板与证明》一文中所述&#xff0c;p的投影点p’就是a x ⃗ \vec x x &#xff08;直线的点向式&#xff09;&#xff0c;所以我们只要求出 x ⃗ \vec x x 就能…

基于单片机的二维码LCD显示控制设计

**单片机设计介绍&#xff0c;基于单片机的二维码LCD显示控制设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的二维码LCD显示控制设计是一个集硬件、软件与通信于一体的综合性项目。此设计的主要目标是实现单片机…