-- 创建虚拟列表类
local VirtualList = class("VirtualList")
---@param scrollView ccui.ScrollView
function VirtualList:ctor(scrollView,data,callback, offset)
self.scrollView=scrollView
-- self.scrollView:setClippingEnabled(false)
local model =scrollView:getChildByName("model")
model:setAnchorPoint(cc.p(0,1))
self.itemPrefab =model
model:retain()
self.callback=callback
self.offset=offset or 5
self.itemContent={}
self.scrollView:removeAllChildren()
self.scrollViewSize =self.scrollView:getContentSize()
self.item_h =self.itemPrefab:getContentSize().height
self.item_w =self.itemPrefab:getContentSize().width
self.onEvent=false
self.selectUPId =0
self.selectDownId=0
self:initData(data)
self:creatrViewMaxItems()
self.lastPosition = cc.p(0, -self.content_h+self.scrollViewSize.height)
self.scrollView:onNodeEvent("exit", function()
self:disableNodeEvents()
end)
self.scrollView:setScrollBarEnabled(false)
end
function VirtualList:initData(data)
self.data =data
local col =self:getItemCols()
local row =math.ceil(#self.data / col)
local contentSize = cc.size(self.scrollViewSize.width, row*(self.item_h+self.offset))
self.scrollView:setInnerContainerSize(contentSize)
-- print("设置容易大小",contentSize.height,self.scrollView:getInnerContainerPosition().y)
self.content_h=contentSize.height >=self.scrollViewSize.height and contentSize.height or self.scrollViewSize.height
local maxRow =math.ceil(self.scrollViewSize.height/(self.item_h+self.offset))+1
if maxRow*col<#self.data and not self.onEvent then
self.onEvent=true
self.scrollView:addEventListener(handler(self,self.onScrollViewEvent))
end
end
--更新数据调用
function VirtualList:refreshUI(data,resetList)
self.scrollView:stopAutoScroll()
local old_len =#self.data
local new_len = #data
self.updateing =true
local last_h =self.content_h
local pos =self.scrollView:getInnerContainerPosition()
self:initData(data)
local now_h =self.content_h
local change_h =now_h-last_h
local cy =pos.y-change_h
local type =0
if old_len==new_len then
type=0
local maxRow =math.ceil(self.scrollViewSize.height/(self.item_h+self.offset))+1
if old_len<maxRow*self:getItemCols() then
self.selectUPId=0
resetList=true
end
if resetList then
self.selectUPId=0
cy= -self.content_h+self.scrollViewSize.height
end
self.scrollView:setInnerContainerPosition(cc.p(0,cy))
elseif old_len<new_len then
--增加数据
type =1
local maxRow =math.ceil(self.scrollViewSize.height/(self.item_h+self.offset))+1
if old_len<maxRow*self:getItemCols() then
self.selectUPId=0
end
if resetList then
self.selectUPId=0
cy= -self.content_h+self.scrollViewSize.height
end
self.scrollView:setInnerContainerPosition(cc.p(0,cy))
elseif old_len>new_len then
--减少数据
type =2
self.selectUPId=0 -- 减少必须从0开始
self.scrollView:setInnerContainerPosition(cc.p(0,-self.content_h+self.scrollViewSize.height))
end
self:updateItem(change_h,type,resetList)
self.updateing =false
end
-- 计算行数
function VirtualList:getItemRows()
local col =self:getItemCols()
local row =0
local maxRow =math.ceil(self.scrollViewSize.height/(self.item_h+self.offset))+1
local minRow =math.ceil(#self.data / col)
row =math.min(minRow,maxRow)
return row
end
-- 计算列数
function VirtualList:getItemCols()
local size = self.scrollView:getInnerContainerSize()
local col =math.floor(size.width/(self.item_w+self.offset))
return col
end
--创建可视范围内的item数量
function VirtualList:creatrViewMaxItems()
local index =1
for i = 1, self:getItemRows() do
for j = 1, self:getItemCols() do
local item = self.itemPrefab:clone()
local h =self.content_h- (self.item_h+self.offset)*(i-1)
item:setPosition(cc.p((j - 1) * (self.item_w+self.offset),h))
self.scrollView:addChild(item)
if not self.itemContent[i] then
self.itemContent[i]={}
end
self.itemContent[i][j]=item
if(index>#self.data) then
item:setVisible(false)
break
end
self:updateId(1)
self.callback(item,self.data[self.selectUPId])
index =index +1
end
end
end
--更新可视范围内的item
function VirtualList:updateItem(change_h,type,resetList)
local row =self:getItemRows()
local col =self:getItemCols()
for i = 1, row do
for j = 1, col do
local py =0
local item
if type == 1 then
-- 增加数据
if not self.itemContent[i] then
self.itemContent[i]={}
py=self.itemContent[i-1][1]:getPositionY()-(self.item_h+self.offset)
else
if self.itemContent[i][j] then
py=self.itemContent[i][j]:getPositionY()+change_h
else
py=self.itemContent[i][1]:getPositionY()
end
end
item=self.itemContent[i][j]
if not item then
item = self.itemPrefab:clone()
self.scrollView:addChild(item)
self.itemContent[i][j]=item
self:updateId(1)
if(self.selectUPId>#self.data) then
item:setVisible(false)
else
item:setVisible(true)
self.callback(item,self.data[self.selectUPId])
end
else
local itemId
local maxRow =math.ceil(self.scrollViewSize.height/(self.item_h+self.offset))+1
if self.selectUPId>=maxRow*col then
itemId =self.selectUPId-((row-(i-1))*col-j)
else
self:updateId(1)
itemId =self.selectUPId
end
if(itemId>#self.data) then
item:setVisible(false)
else
item:setVisible(true)
self.callback(item,self.data[itemId])
end
end
item:setPosition(cc.p((j - 1) * (self.item_w+self.offset),py))
elseif type==2 then
--减少数据
item=self.itemContent[i][j]
local h =self.content_h- (self.item_h+self.offset)*(i-1)
item:setPosition(cc.p((j - 1) * (self.item_w+self.offset),h))
if(self.selectUPId>#self.data) then
item:setVisible(false)
else
item:setVisible(true)
end
self:updateId(1)
self.callback(item,self.data[self.selectUPId])
elseif type==0 then
item=self.itemContent[i][j]
local itemId
if not resetList then
itemId =self.selectUPId-((row-(i-1))*col-j)
else
self:updateId(1)
itemId =self.selectUPId
local h =self.content_h- (self.item_h+self.offset)*(i-1)
item:setPosition(cc.p((j - 1) * (self.item_w+self.offset),h))
end
if(itemId>#self.data) then
item:setVisible(false)
else
item:setVisible(true)
self.callback(item,self.data[itemId])
end
end
end
end
end
function VirtualList:onScrollViewEvent(sender,eventType)
if self.updateing then
return
end
if eventType == ccui.ScrollviewEventType.containerMoved then
-- ScrollView滚动中的逻辑处理
local currentPosition = sender:getInnerContainerPosition()
local delta = cc.p(currentPosition.x - self.lastPosition.x, currentPosition.y - self.lastPosition.y)
self:updateItemPos(delta,math.floor(currentPosition.y-self.scrollViewSize.height))
self.lastPosition =currentPosition
end
end
-- type 1 up 2 down
function VirtualList:updateId(type)
local row =self:getItemRows()
local col =self:getItemCols()
if type==1 then
self.selectUPId =self.selectUPId+1
local id=self.selectUPId-(row-1)*col-(col-1)
self.selectDownId =id
-- print("selectUPId 向上 =",self.selectUPId,self.selectDownId)
else
self.selectDownId =self.selectDownId-1
local id=self.selectDownId + row*col-1
self.selectUPId=id
-- print("selectDownId 向下 =",self.selectDownId,self.selectUPId)
end
end
--更新坐标
function VirtualList:updateItemPos(pos,offsetY)
if pos.y==0 then
return
end
if pos.y>0 then
local item=self.itemContent[1][1]
local startY =item:getPositionY()
if -(startY-self.item_h-self.offset)<=offsetY then
local firstElement = table.remove(self.itemContent, 1)
local item =self.itemContent[#self.itemContent][1]
local py1 =item:getPositionY()
py1 =py1-(self.offset+self.item_h)
-- print("移除头部并添加到末尾",self.selectUPId,py1)
for index, value in ipairs(firstElement) do
value:setPositionY(py1)
self:updateId(1)
if(self.selectUPId>#self.data) then
value:setVisible(false)
else
value:setVisible(true)
self.callback(value,self.data[self.selectUPId])
end
end
table.insert(self.itemContent, firstElement)
end
else
local item=self.itemContent[#self.itemContent][1]
local endY =item:getPositionY()+self.scrollViewSize.height
if -(endY)>=offsetY then
local lastElement = table.remove(self.itemContent)
local py1 =self.itemContent[1][1]:getPositionY()
py1 =py1+(self.offset+self.item_h)
-- print("移除末尾并添加到头部",self.selectDownId,py1)
for i = self:getItemCols(), 1,-1 do
local item=lastElement[i]
item:setPositionY(py1)
item:setVisible(false)
self:updateId(2)
if self.selectDownId>=1 then
item:setVisible(true)
self.callback(item, self.data[self.selectDownId])
end
end
table.insert(self.itemContent, 1, lastElement)
end
end
end
-- 退出界面时销毁监听器
function VirtualList:disableNodeEvents()
end
return VirtualList
cocos2d-x lua 利用ScrollView实现虚拟列表
于 2023-09-04 17:14:52 首次发布