Cocos2dx-lua ScrollView[三]高级篇
一.概述
本文缩写说明: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 + 1return 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 = trueself.is_stop = nilendreturn 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 endwith_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 0interval = interval or 1local schedul_hander = self.scheduler:scheduleScriptFunc(function(dt)if self.is_stop then return endif call_back ~= nil thenif limit_time == 1 then self:remove(with_name)elseif limit_time > 1 then limit_time = limit_time - 1endcall_back(dt)endend, interval, false)self.schedulers[with_name] = schedul_handerreturn with_name
end-- 删除一个定时器
function GlobalTimeTicket:remove(with_name)if with_name == nil then return endlocal schedul_hander = self.schedulers[with_name] if schedul_hander ~= nil thenself.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)endself.schedulers = {}
endfunction GlobalTimeTicket:hasTicket(with_name)local schedul_hander = self.schedulers[with_name] if schedul_hander ~= nil thenreturn trueendreturn false
endfunction GlobalTimeTicket:getSchedulers()return self.schedulers
end-- 停止定时器
function GlobalTimeTicket:stop()self.is_stop = trueself:removeAll()
end
3.3 sv封装模块:svCmn
--[[使用例子if not self.svCmn thenlocal 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)endself.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 = parentself.pos = pos or cc.p(0, 0)self.dir = dir or ScrollViewDir.verticalself.start_pos = start_pos or ScrollViewStartPos.topself.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 -- 第一个单元的起点Xself.end_x = self.setting.end_x or self.start_x -- 最后一个单元结束X间隔 如果是nil 默认 和 start_x一致self.start_y = self.setting.start_y or 0 -- 第一个单元的起点Yself.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_widthself.container_height = setting.container_heightself.inner_hight_offset = setting.inner_hight_offset or 0 -- 内容高度偏移值(仅对纵向有效)--横向的只支持一行的..if self.dir == ScrollViewDir.horizontal thenself.row = 1endself:calculationMaxSum()
endfunction svCmn:updateSetting(setting)if not setting then return endfor k,v in pairs(setting) doself[k] = vend
end--desc:计算一下一屏最多创建的个数
function svCmn:calculationMaxSum()local max_sumif self.dir == ScrollViewDir.horizontal then max_sum = (math.ceil(self.size.width / (self.item_width + self.space_x)) + 1) * self.rowelsemax_sum = (math.ceil(self.size.height / (self.item_height + self.space_y)) + 1) * self.colendself.cacheMaxSize = max_sum
endfunction svCmn:createRootWnd()self:setContentSize(self.size)if not tolua.isnull(self.parent) thenself.parent:addChild(self)endself: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()
endfunction svCmn:registerEvent()if self.need_dynamic == true thenself.scroll_view:addEventListener(function(sender, eventType)if eventType == ccui.ScrollviewEventType.containerMoved thenself:checkRectIntersectsRect()if self.checkovercallback thenself.checkovercallback()endendend)end
endfunction svCmn:registerScriptHandlerSingle(func, handlerId)self.handler[handlerId] = func
endfunction svCmn:numberOfCells()local cells = ScrollViewFuncType.NumberOfCellsif not self.handler or not self.handler[cells] then return endreturn self.handler[cells]()
end--刷新每一个cell
function svCmn:updateCellByIndex(cell, index)if not self.handler[ScrollViewFuncType.UpdateCellByIndex] then return endif not cell.index thencell.create_index = indexendprint("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 endprint("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 endself.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)
endfunction svCmn:setBounceEnabled( status )self.scroll_view:setBounceEnabled(status)
end--desc:移动的过程中盘点是否不再可视范围,不再的时候移除掉,放到对象池,并且准备下一次创建
function svCmn:checkRectIntersectsRect()if self.dir == ScrollViewDir.vertical thenif self.start_pos == ScrollViewStartPos.top thenself:checkOverShowByVertical()else-- 支持ScrollViewStartPos.bottom的了 --by lwcself:checkOverShowByVerticalBottom()endelseif self.dir == ScrollViewDir.horizontal thenself:checkOverShowByHorizontal()end
endfunction svCmn:checkOverShowByVertical()if not self.allCellList then return endlocal container_y = self.container:getPositionY()--计算 视图的上部分和下部分在self.container 的位置local bot = -container_ylocal top = self.size.height + botlocal col_count = math.ceil(#self.allCellList/self.col)--下面因为 self.allCellList 是一维数组 所以要换成二维来算--活跃cell开始行数local activeCellStartRow = 1for i=1, col_count dolocal index = 1 + (i-1)* self.collocal cell = self.allCellList[index]activeCellStartRow = iif cell and cell.y - self.item_height * 0.5 <= top thenbreakendend--活跃cell结束行数local activeCellEndRow = col_countif bot > 0 thenfor i = activeCellStartRow, col_count dolocal index = 1 + (i-1)* self.collocal cell = self.allCellList[index]if cell and cell.y + self.item_height * 0.5 < bot thenactiveCellEndRow = i - 1breakendendend-- print("@保留--> top --> :"..top .." self.col:"..self.col)-- print("@保留--> bot --> :"..bot )-- print("@保留--> 开始行: "..activeCellStartRow.."@结束行: "..activeCellEndRow)local max_count = self:numberOfCells()if max_count thenfor i=1, col_count doif i >= activeCellStartRow and i <= activeCellEndRow thenfor k=1, self.col dolocal index = (i-1) * self.col + kif not self.activeCellIdx[index] thenif index <= max_count thenself:updateCellAtIndex(index)self.activeCellIdx[index] = trueendend endelsefor k=1, self.col dolocal index = (i-1) * self.col + kif index <= max_count thenself.activeCellIdx[index] = falseendendendendend
endfunction svCmn:checkOverShowByVerticalBottom()if not self.allCellList then return endlocal container_y = self.container:getPositionY()--计算 视图的上部分和下部分在self.container 的位置local bot = -container_ylocal top = self.size.height + botlocal col_count = math.ceil(#self.allCellList/self.col)--下面因为 self.allCellList 是一维数组 所以要换成二维来算--活跃cell开始行数local activeCellStartRow = col_countfor i=col_count, 1,-1 dolocal index = 1 + (i-1)* self.collocal cell = self.allCellList[index]activeCellStartRow = iif cell and cell.y - self.item_height * 0.5 <= top thenbreakendend--活跃cell结束行数local activeCellEndRow = 1if bot > 0 thenfor i = activeCellStartRow, 1, -1 dolocal index = 1 + (i-1)* self.collocal cell = self.allCellList[index]if cell and cell.y + self.item_height * 0.5 < bot thenactiveCellEndRow = i + 1breakendendend-- print("@保留--> top --> :"..top .." self.col:"..self.col)-- print("@保留--> bot --> :"..bot )-- print("@保留--> 开始行: "..activeCellStartRow.."@结束行: "..activeCellEndRow)local max_count = self:numberOfCells()for i=1, col_count doif i <= activeCellStartRow and i >= activeCellEndRow thenfor k=1, self.col dolocal index = (i-1) * self.col + kif not self.activeCellIdx[index] thenif index <= max_count thenself:updateCellAtIndex(index)self.activeCellIdx[index] = trueendend endelsefor k=1, self.col dolocal index = (i-1) * self.col + kif index <= max_count thenself.activeCellIdx[index] = falseendendendend
endfunction svCmn:checkOverShowByHorizontal()if not self.allCellList then return endlocal container_x = self.container:getPositionX()--计算 视图的左部分和右部分在self.container 的位置local top = -container_x local bot = top + self.size.widthlocal row_count = #self.allCellList--横向的只支持一行--活跃cell开始行数local activeCellStartRow = 1if top > 0 thenfor index=1, row_count dolocal cell = self.allCellList[index]activeCellStartRow = indexif cell and cell.x + self.item_width * 0.5 >= top thenbreakendendend--活跃cell结束行数local activeCellEndRow = row_countfor index = activeCellStartRow, row_count dolocal cell = self.allCellList[index]if cell and cell.x - self.item_width * 0.5 > bot thenactiveCellEndRow = index - 1breakendend-- print("@保留--> top --> :"..top .." self.row:"..self.row)-- print("@保留--> bot --> :"..bot )-- print("@保留--> 开始行: "..activeCellStartRow.."@结束行: "..activeCellEndRow)local max_count = self:numberOfCells()if max_count thenfor index=1, row_count doif index >= activeCellStartRow and index <= activeCellEndRow thenif not self.activeCellIdx[index] thenif index <= max_count thenself:updateCellAtIndex(index)self.activeCellIdx[index] = trueendend elseif index <= max_count thenself.activeCellIdx[index] = falseendendendend
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))
endfunction svCmn:jumpToMove(pos, time, callback)local target_pos = self:checkPosition(pos.x, pos.y)time = time or 1local 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 thencallback()endend)))
end function svCmn:checkPosition(x, y)local _x, _y = self.container:getPositionX(), self.container:getPositionY()if self.dir == ScrollViewDir.horizontal then_x = _x + xelseif self.dir == ScrollViewDir.vertical then_y = _y + yendif _x > 0 then_x = 0elseif _x < (self.size.width - self.container_size.width) then_x = self.size.width - self.container_size.widthendif _y > 0 then_y = 0elseif _y < (self.size.height - self.container_size.height) then_y = self.size.height - self.container_size.heightendreturn cc.p(_x, _y)
end--获取当前容器的y位置
function svCmn:getCurContainerPosY()if self.container and not tolua.isnull(self.container) thenreturn self.container:getPositionY()end
end
--获取当前容器的x位置
function svCmn:getCurContainerPosX()if self.container and not tolua.isnull(self.container) thenreturn self.container:getPositionX()end
endfunction svCmn:setInnerContainer()local number = self:numberOfCells()local container_width = self.container_width or self.size.widthlocal container_height = self.container_height or self.size.heightif self.dir == ScrollViewDir.horizontal then -- 水平if self.container_width == nil thenlocal num = math.ceil(number / self.row)container_width = num * self.item_width + self.end_x + self.start_x + (num - 1) * self.space_xendelseif self.container_height == nil thenlocal 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_offsetendendcontainer_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 thenself.scroll_view:jumpToTop()elseif self.start_pos == ScrollViewStartPos.bottom thenself.scroll_view:jumpToBottom()end
end--刷新当前显示的item数据 (不改变任何位置的,前提数据数量没有改变如果有改变用 reload)
function svCmn:resetCurrentItems()for i,v in pairs(self.activeCellIdx) doif v thenself:updateCellAtIndex(i)endend
end--根据index 刷新对应索引..如果在显示视图内
function svCmn:resetItemByIndex(index)-- bodyif self.activeCellIdx[index] thenself:updateCellAtIndex(index)end
end--获取活跃中的cell对象
function svCmn:getActiveCellList()local list = {}for i,v in pairs(self.activeCellIdx) doif v and self.allCellList[i] and self.allCellList[i].cell thentable.insert(list, self.allCellList[i].cell)endendreturn list
end--获取index索引对应cell(不管是否活跃)
function svCmn:getCellByIndex(index)if self.allCellList[index] and self.allCellList[index].cell thenreturn self.allCellList[index].cellend
end--获取index索引对应cellXY位置(不管是否活跃)
function svCmn:getCellXYByIndex(index)if self.allCellList[index] thenreturn self.allCellList[index].x, self.allCellList[index].y end
end--获取index索引对应cellXY位置(不活跃会返回空)
function svCmn:getActiveCellByIndex(index)if self.activeCellIdx[index] and self.allCellList[index] thenreturn self.allCellList[index].cellend
end
--获取当前容器所在显示窗口的x y位置
function svCmn:getContainerXY()if self.container thenlocal x, y = self.container:getPosition()return x, yend
end--获取当前容器所在显示窗口的x y位置
function svCmn:setContainerXY(x, y)if self.container thenif x and y thenself.container:setPosition(x,y)elseif x thenself.container:setPositionX(x) endif y then self.container:setPositionY(y) end endend
end--根据索引判断是否活跃中
function svCmn:isActiveByIndex(index)if self.activeCellIdx[index] thenreturn trueendreturn false
end
--移动到以选中idenx的位置作为在中间 显示 目前只支持y 方向的
function svCmn:jumpToMoveByIndex(index)if not self.allCellList[index] then return endlocal y = self.allCellList[index].y or 0local pos = self.container_size.height - (y + self.size.height * 0.5 )if pos < 0 thenpos = 0endlocal pos_per = pos * 100 / (self.container_size.height - self.size.height)if pos_per ~= pos_per thenpos_per = 0;endif pos_per > 100 thenpos_per = 100endif pos_per == 100 thenif self.start_pos == ScrollViewStartPos.top thenself:checkOverShowByVertical()elseself:checkOverShowByVerticalBottom()endendself.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 thenself:updateSetting(setting)endlocal old_width , old_height = 0, 0if self.container_size thenold_width = self.container_size.widthold_height = self.container_size.heightendself.allCellList = {}self.activeCellIdx = {}for k, v in ipairs(self.cacheList) do--相当于隐藏v:setPositionX(-10000)end--设置容器大小self:setInnerContainer()local number = self:numberOfCells()if number == 0 thenreturnendfor i = 1, number dolocal cell = nil if i <= self.time_show_index thencell = self:getCacheCellByIndex(i)endlocal count = #self.allCellListlocal x, yif self.position_data_list thenlocal pos = self.position_data_list[count + 1]if pos thenx, y = pos.x, pos.yelsex, y = self:getCellPosition(count + 1) endelsex, y = self:getCellPosition(count + 1)endlocal cellData = {cell = cell, x = x, y = y}table.insert(self.allCellList, cellData)endif self.is_first_init thenself:startTimeTicket()else--如果时间显示索引小于总数 应该显示继续当前定时器 让下面的能显示出来if self.time_show_index <= number thenself:startTimeTicket()endendif is_keep_position then--是否保持当前显示位置local cur_container_x = self.cur_container_x or 0local cur_container_y = self.cur_container_y or 0if self.dir == ScrollViewDir.vertical then --竖方向if self.start_pos == ScrollViewStartPos.top thenlocal temp_height = self.container_size.height - old_heightcur_container_y = cur_container_y - temp_heightendif cur_container_y > 0 thencur_container_y = 0elseif cur_container_y < (self.size.height - self.container_size.height) thencur_container_y = self.size.height - self.container_size.heightendelseif self.dir == ScrollViewDir.horizontal then --横方向if cur_container_x > 0 thencur_container_x = 0elseif cur_container_x < (self.size.width - self.container_size.width) thencur_container_x = self.size.width - self.container_size.widthendendself.container:setPosition(cur_container_x, cur_container_y)self:checkRectIntersectsRect()elseif select_index == nil thenlocal maxRefreshNum if self.dir == ScrollViewDir.horizontal then -- 水平maxRefreshNum = self.cacheMaxSize - self.rowelsemaxRefreshNum = self.cacheMaxSize - self.colendlocal refreshNum = number < maxRefreshNum and number or maxRefreshNumfor i = 1, refreshNum doif i <= self.time_show_index thenself:updateCellAtIndex(i)endself.activeCellIdx[i] = trueendelseself:selectCellByIndex(select_index)endendif self.is_auto_scroll thenlocal cur_max_count = self.cacheMaxSizeif self.dir == ScrollViewDir.horizontal then cur_max_count = cur_max_count - 2 * self.rowelsecur_max_count = cur_max_count - 2 * self.colendif number <= cur_max_count thenself:setClickEnabled(false)elseself:setClickEnabled(true)endend
end--选中index索引对象(如果列表允许 会排序在开始第一位)
function svCmn:selectCellByIndex(index)local index = index or 1if self.allCellList[index] == nil thenindex = 1endif self.allCellList[index] == nil then return end--一屏幕显示的最大数量local maxRefreshNum if self.dir == ScrollViewDir.horizontal then -- 水平maxRefreshNum = self.cacheMaxSize - self.rowelsemaxRefreshNum = self.cacheMaxSize - self.colendlocal number = self:numberOfCells()if number < maxRefreshNum then--不够显示一屏幕if self.time_show_index == 0 thenself.time_show_index = indexendfor i = 1, number doif i <= self.time_show_index thenself:updateCellAtIndex(i)endself.activeCellIdx[i] = trueendelse--列表允许 情况if self.dir == ScrollViewDir.horizontal then -- 水平--容器x方向位置local container_xif index == 1 thencontainer_x = 0elsecontainer_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 thencontainer_x = max_contariner_xendlocal show_index = math.floor(math.abs(container_x) / self.item_width) + 1if self.time_show_index < show_index thenself.time_show_index = show_indexendself.container:setPositionX(container_x)self:checkRectIntersectsRect()else -- 垂直local container_yif index == 1 thencontainer_y = (self.start_y + self.allCellList[index].y + self.item_height * 0.5) - self.size.height elsecontainer_y = (self.allCellList[index].y + (self.item_height + self.space_y) * 0.5) - self.size.height endif container_y < 0 thencontainer_y = 0endlocal index_1 = math.floor( (self.container_size.height - (container_y + self.size.height)) / self.item_height) + 1local show_index = (index_1 - 1) * self.col + 1if self.time_show_index < show_index thenself.time_show_index = show_indexendself.container:setPositionY(- container_y)self:checkRectIntersectsRect()endendif index > 0 and index <= self:numberOfCells() thenlocal cell = self:getCacheCellByIndex(index)cell.index = indexself.allCellList[index].cell = cellself:onCellTouched(cell, index)end
endfunction svCmn:setOnCellTouched(index)local cell = self:getCacheCellByIndex(index)cell.index = indexself.allCellList[index].cell = cellself:onCellTouched(cell, index)
endfunction svCmn:startTimeTicket()if self.time_ticket == nil thenif #self.allCellList == 0 thenreturnend--到时间显示的索引local once_num = self.once_num or 1local _callback = function()if tolua.isnull(self.container) then return endlocal count = self.time_show_index + once_numlocal index = self.time_show_index + 1if index == 0 thenindex = 1endlocal size = #self.allCellListself.time_show_index = self.time_show_index + once_numfor i = index, count doif i > size then--超过总数了breakendlocal cellData = self.allCellList[i]if cellData and cellData.cell == nil thencellData.cell = self:getCacheCellByIndex(i)endif self.activeCellIdx[i] thenself:updateCellAtIndex(i)endendif self.time_show_index >= size thenself:clearTimeTicket()self.is_first_init = falseendendself.time_ticket = GlobalTimeTicket:getInstance():add(_callback, self.delay / display.DEFAULT_FPS)end
endfunction svCmn:clearTimeTicket()if self.time_ticket ~= nil thenGlobalTimeTicket:getInstance():remove(self.time_ticket)self.time_ticket = nilend
end function svCmn:getCellPosition(index)local cur_item_index = indexlocal anchor_point = cc.p(0.5,0.5)local _x, _y = 0, 0if 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))elseif 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)endendreturn _x, _y
end--通过创建或复用的方式,获取index对应的cell对象
function svCmn:getCacheCellByIndex(index)local cacheIndex = (index - 1) % self.cacheMaxSize + 1if not self.cacheList[cacheIndex] thenlocal newCell = self:createNewCell(index)if newCell thennewCell:setAnchorPoint(cc.p(0.5, 0.5))newCell:setPositionX(-10000)--隐藏self.cacheList[cacheIndex] = newCellself.scroll_view:addChild(newCell)endreturn newCellelsereturn self.cacheList[cacheIndex]end
end--cell设置位置,并刷新cell的UI
function svCmn:updateCellAtIndex(index)if index > self.time_show_index thenreturnendif not self.allCellList[index] then return endlocal cellData = self.allCellList[index]if cellData.cell == nil thencellData.cell = self:getCacheCellByIndex(index) --self.allCellList的cell赋值在这里endcellData.cell:setPosition(cellData.x, cellData.y)self:updateCellByIndex(cellData.cell, index)
endfunction svCmn:clearTimeTicket()if self.time_ticket ~= nil thenGlobalTimeTicket:getInstance():remove(self.time_ticket)self.time_ticket = nilend
end function svCmn:getMaxSize()return self.container_size
endfunction svCmn:getContainer()return self.container
endfunction svCmn:scrollToPercentVertical( percent, time )if percent ~= percent then percent = 0 endself.scroll_view:scrollToPercentVertical(percent, time, true)
endfunction svCmn:DeleteMe()doStopAllActions(self.container)self:clearTimeTicket()for k, item in ipairs(self.cacheList) doif item.DeleteMe thenitem:DeleteMe()endendself.allCellList = nilself.activeCellIdx = nilself.cacheList = nilself: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 thenlocal 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)endself.svCmn:reloadData()
endfunction ModuleTest:createNewCell(width, height)local cell = ActionCommonItem.new()return cell
endfunction ModuleTest:numberOfCells()if not self.cellData then return 0 endreturn #self.cellData
endfunction ModuleTest:updateCellByIndex(cell, index)local onecellData = self.cellData[index]cell.root_wnd:getChildByName("label"):setString(onecellData.index)
end
相关文章:

Cocos2dx-lua ScrollView[三]高级篇
一.概述 本文缩写说明:sv ScrollView, cell代表ScrollView的一个子节点 本文介绍sv的一种封装类库,来实现快速创建sv,有如下几个优点: 1.item的位置通过参数控制,提高开发效率 2.免去了调用sv的API,提…...

后端之卡尔曼滤波
后端之卡尔曼滤波 前言 在很久之前,人们刚结束信息传递只能靠信件的时代,通信技术蓬勃发展,无线通信和有线通信走进家家户户,而著名的贝尔实验室就在这个过程做了很多影响深远的研究。为了满足不同电路和系统对信号的需求&#…...

Docker 夺命连环 15 问
目录 什么是Docker? Docker的应用场景有哪些? Docker的优点有哪些? Docker与虚拟机的区别是什么? Docker的三大核心是什么? 如何快速安装Docker? 如何修改Docker的存储位置? Docker镜像常…...

2024最新版克魔助手抓包教程(9) - 克魔助手 IOS 数据抓包
引言 在移动应用程序的开发中,了解应用程序的网络通信是至关重要的。数据抓包是一种很好的方法,可以让我们分析应用程序的网络请求和响应,了解应用程序的网络操作情况。克魔助手是一款非常强大的抓包工具,可以帮助我们在 Android …...

Spring Boot 防止XSS攻击
XSS 跨站脚本工具(cross 斯特scripting),为不和层叠样式表(cascading style sheets,CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往web页面里插入恶意ScriptScript代码,当用户浏览该页…...

aidl文件生成Java、C++[android]、C++[ndk]、Rust接口
目录 前言一、Java二、C[android]三、C[ndk]四、Rust接口 前言 在 Android 开发中,AIDL 文件通常会被自动编译,生成对应语言的接口文件。对于应用层 Java 开发者来说,使用 AIDL 和 Binder 封装的接口可以让他们更加专注于应用逻辑࿰…...

多源统一视频融合可视指挥调度平台VMS/smarteye系统概述
系统功能 1. 集成了视频监控典型的常用功能,包括录像(本地录像、云端录像(录像计划、下载计划-无线导出)、远程检索回放)、实时预览(PTZ云台操控、轮播、多屏操控等)、地图-轨迹回放、语音对讲…...

PyTorch简介:与TensorFlow的比较
PyTorch简介:与TensorFlow的比较 一、PyTorch框架概述 PyTorch是一个开源的机器学习库,广泛用于计算机视觉和自然语言处理。由Facebook的人工智能研究团队开发,它以其灵活性和动态计算图而闻名。 主要特点 动态计算图:PyTorch…...

虚拟机-从头配置Ubuntu18.04(包括anaconda,cuda,cudnn,pycharm,ros,vscode)
最好先安装anaconda后cuda和cudnn,因为配置环境的时候可能conda会覆盖cuda的路径(不确定这种说法对不对,这里只是给大家的建议) 准备工作: 1.Ubuntu18.04,x86_64,amd64 虚拟机下载和虚拟机Ubu…...

uniApp使用XR-Frame创建3D场景(8)粒子系统
上篇文章讲述了如何将XR-Frame作为子组件集成到uniApp中使用 本片我们详细讲解一下xr-frame的粒子系统 先看源码 <xr-scene render-system"alpha:true" bind:ready"handleReady"> <xr-node visible"{{sec8}}"><xr-asset-load t…...

【JMeter入门】—— JMeter介绍
1、什么是JMeter Apache JMeter是Apache组织开发的基于Java的压力测试工具,用于对软件做压力测试。它最初被设计用于Web应用测试,但后来扩展到其他测试领域。 (Apache JMeter是100%纯JAVA桌面应用程序)Apache JMeter可以用于对静…...

C# 多线程编程:线程锁与无锁并发
文章目录 前言一、锁的基本概念1.1 什么是锁?1.2 为什么需要锁?1.3 锁的作用原理 二、线程锁的类型2.1 自旋锁(Spin Lock)2.2 互斥锁(Mutex)2.3 混合锁(Hybrid Lock)2.4 读写锁&…...

React.FC
React.FC 是 React 中的一个类型别名,代表“函数组件”。它是一个接受 props(属性)并返回 JSX 元素的函数。 type React.FC<P {}> (props: P) > ReactElement | null;其中:P 是一个可选的泛型类型参数,表示…...

使用pytorch构建一个无监督的深度卷积GAN网络模型
本文为此系列的第二篇DCGAN,上一篇为初级的GAN。普通GAN有训练不稳定、容易陷入局部最优等问题,DCGAN相对于普通GAN的优点是能够生成更加逼真、清晰的图像。 因为DCGAN是在GAN的基础上的改造,所以本篇只针对GAN的改造点进行讲解,其…...

[AI]文心一言出圈的同时,NLP处理下的ChatGPT-4.5最新资讯
AI文心一言出圈的同时,NLP处理下的ChatGPT-4.5最新资讯 1.背景介绍 随着人工智能技术的不断发展,自然语言处理(NLP)技术在近年来取得了显著的进步。其中,聊天机器人技术作为NLP领域的一个重要应用,已经广…...

vue.js设计与实现(分支切换与cleanup)
如存在三元运算符时,怎么处理 // 原始数据 const data { text: hello world,ok:true}// 副作用函数存在三元运算符 effect(function effectFn(){document.body.innerText obj.ok ? obj.text : not })// 理解如此,obj.ok和obj.text都会绑定effectFn函…...

206基于matlab的无人机航迹规划(UAV track plannin)
基于matlab的无人机航迹规划(UAV track plannin)。输入输出参数包括 横滚、俯仰、航向角(单位:度);横滚速率、俯仰速率、航向角速率(单位:度/秒);飞机运动速度——X右翼、…...

【Linux 】查看veth-pair对的映射关系
1. 查看当前存在的ns ip netns add netns199 //新建一个命名空间 # ip netns show netns199 (id: 3)可以看到一个名称叫做netns199 的命名空间,其 id为3 2. 创建一个对,并加入其中一个到其他命名空间中 $ sudo ip link add veth100 type veth peer n…...

Cisco Firepower FMCv修改管理Ip方法
FMCv 是部署在VMWARE虚拟平台上的FMC 部署完成后,如何修改管理IP 1 查看当前版本 show version 可以看到是for VMware 2 修改管理IP步骤 2.1 进入expert模式 expert2.2 进入超级用户 sudo su并输入密码 2.3 查看当前网卡Ip 2.4 修改Ip 命令: /…...

PHP开发全新29网课交单平台源码修复全开源版本,支持聚合登陆易支付
这是一套最新版本的PHP开发的网课交单平台源代码,已进行全开源修复,支持聚合登录和易支付功能。 项目 地 址 : runruncode.com/php/19721.html 以下是对该套代码的主要更新和修复: 1. 移除了论文编辑功能。 2. 移除了强国接码…...

【Web前端】CSS基本语法规范和引入方式常见选择器用法常见元素属性
一、基本语法规范 选择器 {一条/N条声明} 选择器决定针对谁修改 (找谁) 声明决定修改什么.。(干什么) 声明的属性是键值对.。使用 : 区分键值对, 使用 : 区分键和值。 <!DOCTYPE html> <html lang"en"> <head>&…...

SnapGene 5 for Mac 分子生物学软件
SnapGene 5 for Mac是一款专为Mac操作系统设计的分子生物学软件,以其强大的功能和用户友好的界面,为科研人员提供了高效、便捷的基因克隆和分子实验设计体验。 软件下载:SnapGene 5 for Mac v5.3.1中文激活版 这款软件支持DNA构建和克隆设计&…...

本地部署大模型的几种工具(上-相关使用)
目录 前言 为什么本地部署 目前的工具 vllm 介绍 下载模型 安装vllm 运行 存在问题 chatglm.cpp 介绍 下载 安装 运行 命令行运行 webdemo运行 GPU推理 ollama 介绍 下载 运行 运行不同参数量的模型 存在问题 lmstudio 介绍 下载 使用 下载模型文件…...

Spring Boot集成itext实现html生成PDF功能
1.itext介绍 iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件 iText 的特点 以下是 iText 库的显着特点 − Interactive − iText 为你提供类(API)来生成…...

Java 多态、包、final、权限修饰符、静态代码块
多态 Java多态是指一个对象可以具有多种形态。它是面向对象编程的一个重要特性,允许子类对象可以被当作父类对象使用。多态的实现主要依赖于继承、接口和方法重写。 在Java中,多态的实现主要通过以下两种方式: 继承:子类继承父类…...

基于Spring boot + Vue协同过滤算法的电影推荐系统
末尾获取源码作者介绍:大家好,我是墨韵,本人4年开发经验,专注定制项目开发 更多项目:CSDN主页YAML墨韵 学如逆水行舟,不进则退。学习如赶路,不能慢一步。 目录 一、项目简介 二、开发技术与环…...

Chrome之解决:浏览器插件不能使用问题(十三)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒…...

【正版特惠】IDM 永久授权 优惠低至109元!
尽管小编有修改版IDM,但是由于软件太好用了,很多同学干脆就直接购买了正版,现在正版也不贵,并且授权码绑定自己的邮箱,直接官方下载激活,无需其他的绿化修改之类的操作,不喜欢那么麻烦的&#x…...

SpringBoot与Prometheus监控整合
参考: springboot实战之prometheus监控整合-腾讯云开发者社区-腾讯云 https://www.cnblogs.com/skevin/p/15874139.html https://www.jianshu.com/p/e5dc2b45c7a4...

Linux 系统 docker搭建LNMP环境
1、安装nginx docker pull nginx (默认安装的是最新版本) 2、运行nginx docker run --name nginx -p 80:80 -d nginx:latest 备注:--name nginx 表示容器名为 nginx -d 表示后台运行 -p 80:80 表示把本地80端口绑定到Nginx服务端的 80端口 nginx:lates…...