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. 移除了强国接码…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...