先上代码
local CustomListView = class("CustomListView", cc.Node);
--==============================--
--author:{SYL}
--desc: 列表 目前就实现垂直滑动的
--time:2019-03-13 08:47:57
--@_scrollView: studio中拼的scrollView
--@_itemTemplate: 每一项的模版
--@_itemData: 总数据 格式是数组
--@_gridNum: 一行的数量
--@_spacingX: x间隔 目前没用是自动算的
--@_spacingY: y间隔
--@return
--==============================--
function CustomListView:ctor(_scrollView,_itemTemplate,_itemData,_gridNum,_spacingX,_spacingY)
self._scrollView = _scrollView;
self._itemTemplate = _itemTemplate;
self._itemData = _itemData;
self._gridNum = _gridNum or 1;
self._spacingX = _spacingX or 0;
self._spacingY = _spacingY or 0;
self._spawnCount = _spawnCount;
self._totalCount = _totalCount;
self._bufferZone = _bufferZone;
--逻辑参数,一般在外面要用到的一些值都放再这里,项中也可以取到
self.logicParams = {};
self:onUpdate(function(dt)
self:updateAllItem(dt);
end)
self:initialize();
end
--==============================--
--author:{SYL}
--desc:清空数据 初始化
--time:2019-03-15 04:15:20
--@logicParams:因为逻辑数据也会清空 需要重新传入
--@return
--==============================--
function CustomListView:reset(logicParams)
self:stopScroll();
self:setTouchEnabled(false);
self._itemData = {};
self.logicParams = {};
for k,v in pairs(logicParams) do
self.logicParams[k] = v;
end
self:initialize();
self:setTouchEnabled(true);
end
--==============================--
--author:{SYL}
--desc: 获取真实item的个数 计算获得 如果有需要可以继承重写此方法
--time:2019-03-15 04:16:12
--@return
--==============================--
function CustomListView:getSpawnCount()
local long = self._svContentSize.height;
local longItem = self._itemContentSize.height;
local num = math.floor(2*long/(longItem+self._spacingY));
return num*self._gridNum;
end
--==============================--
--author:{SYL}
--desc: 滑动重置项的长度,有特殊需要可以继承重写此方法
--time:2019-03-15 04:16:56
--@return
--==============================--
function CustomListView:getBufferZone()
local long = self._svContentSize.height;
return long;
end
--==============================--
--author:{SYL}
--desc: 重置项的数据 目前支持原来数据为0或者是一页 或者大于self:getSpawnCount()
--当原数据个数大于一页小于self:getSpawnCount()时 重置的时候会把页面刷到顶上,之后有需求可以再完善
--传入的itemData比原来的大
--time:2019-03-15 04:17:43
--@_itemData: 传入的全部数据
--@return
--==============================--
function CustomListView:reloadItemData(_itemData)
if #self._itemData<self:getSpawnCount() then
self._itemData = _itemData;
self:initialize();
else
self._itemData = _itemData;
self:refreshItems();
end
end
--==============================--
--author:{SYL}
--desc: 初始化项,清空列表中的节点,根据数据重新生成节点,一般外部不需要调用
--time:2019-03-15 04:29:16
--@return
--==============================--
function CustomListView:initialize()
self._content = self._scrollView:getInnerContainer();
self._svContentSize = self._scrollView:getContentSize();
local tempItem = self._itemTemplate:create();
self._itemContentSize = tempItem:getContentSize();
self._spawnCount = self:getSpawnCount();
self._bufferZone = self:getBufferZone();
self._content:removeAllChildren();
self._items = {};
self._updateTimer = 0;
self._updateInterval = 0;
self._lastContentPosY = 0;
self._totalCount = #self._itemData;
self._spawnCount = self._totalCount>self._spawnCount and self._spawnCount or self._totalCount;
self._contentSize = self._content:getContentSize();
local height = math.ceil(self._totalCount/self._gridNum) * (self._itemContentSize.height + self._spacingY) + self._spacingY;
height = height < self._svContentSize.height and self._svContentSize.height or height;
self._scrollView:setInnerContainerSize(cc.size(self._contentSize.width,height));
self._contentSize = self._content:getContentSize();
--self:scrollToBegin()
local gridNum = self._gridNum;
for i=1,self._spawnCount do
local item = self._itemTemplate:create();
self._content:addChild(item);
local hindex = math.floor((i-1)/gridNum);
local gridIndex = (i-1)%gridNum;
local dx = self._contentSize.width/gridNum;
local x = dx/2+gridIndex*dx;
local y = -self._itemContentSize.height * (0.5 + hindex) - self._spacingY * (hindex + 1);
item:setPosition(cc.p(x, y+self._contentSize.height));
item.itemID = i;
item.logicParams = self.logicParams;
self:updateItem(item);
self._items[i] = item;
end
end
--==============================--
--author:{SYL}
--desc:获取项相对于scrollView中心的坐标,一般外部不需要调用
--time:2019-03-15 04:30:21
--@item:项
--@return
--==============================--
function CustomListView:getPositionInView(item)
local worldPos = item:getParent():convertToWorldSpace(cc.p(item:getPosition()));
local viewPos = self._scrollView:convertToNodeSpace(worldPos);
--转换成scrollView锚点在0.5,0.5的坐标
viewPos.y = viewPos.y-0.5*self._svContentSize.height;
viewPos.x = viewPos.x+0.5*self._svContentSize.width;
return viewPos;
end
--==============================--
--author:{SYL}
--desc: 更新项,封装一层便于继承修改
--time:2019-03-14 01:54:02
--@item: 需要更新的项
--@return
--==============================--
function CustomListView:updateItem(item,bManual)
item:updateItem(self._itemData[item.itemID],item.itemID,self._itemData,bManual);
item:setVisible(self._itemData[item.itemID] and true or false);
end
--==============================--
--author:{SYL}
--desc:手动刷新每一项,根据需求调用,一般滑动的时候会自动刷新
--time:2019-03-15 04:31:38
--@return
--==============================--
function CustomListView:refreshItems()
local items = self._items;
for i,v in ipairs(items) do
self:updateItem(v,true);
end
end
--==============================--
--author:{SYL}
--desc:更新项的方法 在发生滑动的时候判断然后更新要更新的项,内部调用,可以设置判断时间,目前是每帧都判断
--time:2019-03-15 04:32:20
--@dt:
--@return
--==============================--
function CustomListView:updateAllItem(dt)
if not self._items then
return;
end
self._updateTimer = self._updateTimer+dt;
if self._updateTimer < self._updateInterval then
return;
end
self._updateTimer = 0;
local items = self._items;
local buffer = self._bufferZone;
-- 如果当前content的y坐标小于上次记录值,则代表往下滚动,否则往上。
local isDown = self._content:getPositionY() < self._lastContentPosY; --scrolling direction;
local isUp = self._content:getPositionY() > self._lastContentPosY;
-- 实际创建项占了多高(即它们的高度累加)
local offset = (self._itemContentSize.height+self._spacingY)*math.ceil(#items/self._gridNum);
--遍历数组,更新item的位置和显示
for i=1,#items do
local item = items[i];
local viewPos = self:getPositionInView(item);
if isDown then
--如果往下滚动时item已经超出缓冲矩形,且items[i].y + offset未超出content上边界,
--则更新item的坐标(即上移了一个offset的位置),同时更新item的显示内容
local py = item:getPositionY();
py = py-self._contentSize.height;
if viewPos.y < -buffer and py + offset < 0 then
item:setPositionY(item:getPositionY()+offset);
local itemId = item.itemID - #items; --update item id
item.itemID = itemId;
self:updateItem(item);
end
elseif isUp then
--如果往上滚动时item已经超出缓冲矩形,且newY未超出content下边界,
--则更新item的坐标(即下移了一个offset的位置),同时更新item的显示内容
local py = item:getPositionY()
py = py-self._contentSize.height
if viewPos.y > buffer and py - offset > -self._contentSize.height then
item:setPositionY(item:getPositionY()-offset)
local itemId = item.itemID + #items; --update item id
item.itemID = itemId;
self:updateItem(item);
end
end
end
--update lastContentPosY
self._lastContentPosY = self._content:getPositionY();
--自动可以点击滑动
if not self:isTouchEnabled() then
if self._autoTouchEnabledTime then
self._autoTouchEnabledTimeCount = self._autoTouchEnabledTime and self._autoTouchEnabledTime+dt or dt
if self._autoTouchEnabledTimeCount>self.self._autoTouchEnabledTime then
self._autoTouchEnabledTimeCount = self._autoTouchEnabledTimeCount - self.self._autoTouchEnabledTime;
self:setTouchEnabled(true);
end
end
end
end
--==============================--
--author:{SYL}
--desc:添加一项,想实现无感的往后面加一项,不会造成滑动,目前因为原来的scrollview只要改变了里面节点的size就会滑动所以没有实现,后面按需求完善
--time:2019-03-15 04:34:53
--@return
--==============================--
function CustomListView:addItem()
--self._totalCount = self._totalCount + 1;
--local height = math.ceil(self._totalCount/self._gridNum) * (self._itemContentSize.height + self._spacingY) + self._spacingY;
--self._scrollView:setInnerContainerSize(cc.size(self._contentSize.width,height));
end
--==============================--
--author:{SYL}
--desc: 滑到顶部,跳到顶部,滑到指定位置等方法后续再实现
--time:2019-03-13 08:56:50
--@stime: 活动时间
--@attenuated: 速度减弱true false
--@return
--==============================--
function CustomListView:scrollToBegin(stime,attenuated)
--self._scrollView:scrollToPercentVertical(0,stime or 0,attenuated);
end
--==============================--
--author:{SYL}
--desc:停止滑动
--time:2019-03-15 04:38:59
--@return
--==============================--
function CustomListView:stopScroll()
self._scrollView:stopAutoScroll();
self._scrollView:stopScroll();
end
--==============================--
--author:{SYL}
--desc:设置点击状态,false就点不了,也就不能滑动了
--time:2019-03-15 04:39:10
--@b:
--@return
--==============================--
function CustomListView:setTouchEnabled(b)
self._scrollView:setTouchEnabled(b);
self._autoTouchEnabledTime = nil;
end
--==============================--
--author:{SYL}
--desc:是否可点击/可滑动
--time:2019-03-15 04:39:39
--@return
--==============================--
function CustomListView:isTouchEnabled()
return self._scrollView:isTouchEnabled();
end
--==============================--
--author:{SYL}
--desc:设置不可滑动后,可以调这个方法,传入时间,一定时间后自动可以滑动
--time:2019-03-15 04:40:15
--@autoTime: 时间S
--@return
--==============================--
function CustomListView:setAutoTouchEnabled(autoTime)
self._autoTouchEnabledTime = autoTime;
end
return CustomListView
---------------------------------------------------------------------------------------
local CustomListViewItem = class("CustomListViewItem", ccui.Widget);
--==============================--
--author:{SYL}
--desc: 自定义列表组件的项 一般继承使用
--time:2019-03-15 04:41:53
--@csbFile:
--@return
--==============================--
function CustomListViewItem:ctor(csbFile)
if not csbFile then
return;
end
self._ui = helper.loadUIQuick(self, csbFile,false,true);
self:setContentSize(self._lytUi:getContentSize());
self._ui:setPosition(cc.p(self._lytUi:getContentSize().width/2,self._lytUi:getContentSize().height/2));
self._lytUi:setSwallowTouches(false);
end
function CustomListViewItem:onItemTouch(sender,event)
if 0 == event then
local worldPos = sender:getParent():convertToWorldSpace(cc.p(sender:getPosition()));
self._touchBeganPos = worldPos;
elseif 2 == event then
local worldPos = sender:getParent():convertToWorldSpace(cc.p(sender:getPosition()));
if math.abs(worldPos.y-self._touchBeganPos.y)>5 or math.abs(worldPos.x-self._touchBeganPos.x)>5 then
else
self:onItemClick();
end
end
end
function CustomListViewItem:onItemClick()
print(self.itemID);
end
function CustomListViewItem:updateItem(data,itemID,itemData)
helper.setString(self._lyText, itemData);
end
return CustomListViewItem
网络请求方面,先定义好每页的数据,比如10条,第一次向服务端请求第一页数据 page=1,服务端返回总的数据数和10条数据
如 {len=100,data={1,2,3,4,5,6,7,8,9,10}},我先请求了2页,itemData前10项是数据,后90项用false填充,滑动的时候,滑到数据为false,就根据数据id,如11计算math.ceil(21/10),确定应该要请求第三页数据了,更新22就不用重复请求了,这里要用一个已经请求的表来控制,如果请求失败还要重新请求等,如果滑快了,先请求了第十页数据,上滑再请求第五页数据等,只要服务端返回的len个数,和每次的page和data个数确定,就可以比较完美的实现数据分次加载的listview了。