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. 移除了强国接码…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
