1、引言
项目中用到一个展示Vip等级说明的需求,要做到类似翻滚一页展示一页的详情,可以无限滑动。这时候,下意识想到可以用pageView。这次想着一次做个通用的,以后需要直接拿来套用就好!
2、问题分析
我们周围常见的广告很多都是这样的,很多还是自动翻页,看上去很酷!我们假设有很多页面。我们分析PageView控件,这个控件就是翻页滑动。只要大于一页就可以滑动翻页,这个控件的页码索引是从0开始的。我们的页数展示总的分为三种情况:
假设有n页:
- n = 1: 这种情况无需处理
- n = 2:创建三页,Page0、Page1、Page2,其中Page1显示第一页的信息,Page0和Page2显示第二页的信息。初始化时强制滑动到第一页,显示第一页信息。
- n >= 3:创建三页,Page0、Page1、Page2,其中Page1显示第一页的信息,Page0显示第n页的信息、Page2显示第二页的信息。初始化时强制滑动到第一页,显示第一页信息。
通过观察上面的三种情况,其实可以再次合并:
- n = 1: 这种情况无需处理
- n >= 2:创建三页,Page0、Page1、Page2,其中Page1显示第一页的信息,Page0显示第n页的信息、Page2显示第二页的信息。初始化时强制滑动到第一页,显示第一页信息。
朋友们,你们觉得这样合理?下面我们来上代码
3、代码
图片这里就不提供了,数据准备如下:
local VipLevelInfo = {
{level = 0, almsMoneyDesc = "福利0"},
{level = 1, almsMoneyDesc = "福利1"},
{level = 2, almsMoneyDesc = "福利2"},
{level = 3, almsMoneyDesc = "福利3"},
{level = 4, almsMoneyDesc = "福利4"},
{level = 5, almsMoneyDesc = "福利5"},
}
我们这里
function VipPrivilegeLayer:updateCircle()
local curIndex = self.pageViewVip:getCurPageIndex()
local curPage = self.pageViewVip:getPage(curIndex)
local curTag = curPage:getTag()
for k,v in ipairs(self.imgList) do
if curTag == k then
v:loadTexture("yuan_01.png")
else
v:loadTexture("yuan_02.png")
end
end
end
-- pageView初始化
function VipPrivilegeLayer:initVipPrivilegeDesc()
print("VipPrivilegeLayer:initVipPrivilegeDesc")
-- 删除原来的页面 由于我的模板做在pageView的外面
self.pageViewVip:removeAllPages()
-- 这样也可以删除原来的页面,如果是模板是第0页,改为self.pageViewVip:removePageAtIndex(i) 就好了
-- local pagesAll = self.pageViewVip:getPages()
-- for i = #pagesAll, 1, -1 do
-- self.pageViewVip:removePageAtIndex(i-1)
-- end
-- 总页数
local pages = #VipLevelInfo
-- 默认是往左滑动
self.lastScrollDir = "left"
self.infoIndex = pages
-- 设置滑动灵敏度
self.pageViewVip:setCustomScrollThreshold(30)
-- 监听滑动事件
self.pageViewVip:addEventListener(
function(sender, eventType)
print("-------eventType:", eventType)
if eventType == ccui.PageViewEventType.turning then
self:onPageViewEvent(self.infoIndex,self.lastScrollDir)
-- 滑动后下面小圆点的状态刷新
self:updateCircle()
end
end)
-- 初始化小圆点
if pages > 1 then
self.imgList = {}
local mid =(1 + pages) / 2
for k = 1,pages do
local img = ccui.ImageView:create("yuan_02.png")
-- 我们这里的屏幕宽度是1136
img:setPosition(cc.p(1136 / 2 + 30 *(k - mid), 100))
img:addTo(self)
self.imgList[#self.imgList + 1] = img
end
end
if 1 == pages then
self:addNewPage(1, 0)
elseif pages >= 2 then
self:addNewPage(pages, 0)
self:addNewPage(1, 1)
self:addNewPage(2, 2)
self.imgList[1]:loadTexture("yuan_01.png")
self.pageViewVip:scrollToPage(1)
end
end
添加新的一页:
-- @pageTag: 该页显示的内容索引(数组的索引1 ~ pages)
-- @pageIdx: 插入位置(pageView的页码索引)
function VipPrivilegeLayer:addNewPage(pageTag, pageIdx)
print("==========>>>>. pageTag = "..pageTag)
local newPage = nil
-- 如果第0页是模板
-- if not bClone then
-- newPage = self.pageViewVip:getPage(0)
-- else
-- newPage = self.pageViewVip:getPage(0):clone()
-- end
-- 模板页
newPage = self.panelVip:clone()
newPage:setTag(pageTag)
-- 刷新该页 pageTag是数组的索引
self:updatePage(newPage,pageTag)
-- 在索引位置插入新页
self.pageViewVip:insertPage(newPage, pageIdx)
end
监听滑动事件这个很关键(滑动就要创建新页面):
-- 滑动响应处理
-- @scrollDir: 滑动方向
-- @infoIndex: 索引
function VipPrivilegeLayer:onPageViewEvent(infoIndex,scrollDir)
-- 总页数
local pages = #VipLevelInfo
-- 当前页
local curPage = self.pageViewVip:getCurPageIndex()
local pageIdx = 0
local pageInfoIndex = infoIndex
-- 上次的滚动方向
local lastScrollDir = scrollDir
if pages >= 2 then
if 0 == curPage then
print("111 pageInfoIndex = "..pageInfoIndex)
pageInfoIndex = pageInfoIndex - 1
if pageInfoIndex <= 0 then
pageInfoIndex = pages
end
-- 方向相反
if lastScrollDir == "right" then
pageInfoIndex = pageInfoIndex - 2
if pageInfoIndex <= 0 then
pageInfoIndex = pages
end
end
lastScrollDir = "left"
pageIdx = pageIdx - 1
if pageIdx <= 0 then
pageIdx = pages
end
self.pageViewVip:removePageAtIndex(2)
self:addNewPage(pageInfoIndex, 0)
elseif 2 == curPage then
-- 需要刷新的信息
pageInfoIndex = pageInfoIndex + 1
if pageInfoIndex > pages then
pageInfoIndex = 1
end
if lastScrollDir == "left" then
pageInfoIndex = pageInfoIndex + 2
if pageInfoIndex > pages then
pageInfoIndex = 1
end
end
lastScrollDir = "right"
pageIdx = pageIdx + 1
if pageIdx > pages then
pageIdx = 1
end
self.pageViewVip:removePageAtIndex(0)
self:addNewPage(pageInfoIndex, 2)
end
-- 强制滑动到第1页
self.pageViewVip:scrollToPage(1)
self.infoIndex = pageInfoIndex
self.lastScrollDir = lastScrollDir
end
end
还可以在两边加两个箭头,两边的按钮只要左边强制滑动第0页,右边滑到第2页,就可以了。pageView滑动后会自动响应滑动事件来刷新前一页或后一页。
4、效果及问题
运行代码后,果真实现了。不过你多次打开或者关闭,有没有发现一个问题,每次这个第一页都出现了偏移。这里笔者也碰到这个坑。原因我们就不做具体分析了,给出链接,大家仔细看。这里也贴出来,方便一些:
*详细情况如下:
- PageView调用scrollToPage出现页面偏移
- cocos2dx:3.4, cocos:2.1.5
本来想调用PageView的scrollToPage来设置默认页的,但发现PageView如果过早调用scrollToPage函数(比如在刚刚初始化完PageView,或者刚刚读取完csb),会出现页面偏移现象(除了索引为0的Page)。
原因分析如下,前提是刚初始化完PageView,而且里面已经有Page:
- 1.调用scrollToPage会设置了当前页的索引(_curPageIdx)
- 2.PageView还没开始调用doLayout(在drawScene里调用, doLayout要完成3项工作:更新所有页面位置,更新所有页面大小,更新边界页)
- 3.此时PageView已经开始Update,而且Update先于drawScene
- 4.PageVew在Update的autoScroll里开始更新每个Page的位置,_autoScrollDistance已经开始减少,但是边界也还没初始化,页面不动(出错1)
- 5.由于drawScene里面调用了doLayout,dolayout先开始它的3项工作,由于第一步的时候已经设置了_curPageIdx,所以第一项工作的时候,已经把页面位置更新到当前索引页位置(出错2)
- 6.由于autoScrollDistance还没为0,后续的Update中都是在第5步的页面位置上进行update,位置已经不正确了
5、解决办法(scrollToPage()偏移问题)
调用scrollToPage后会设置 _isAutoScrolling = true;而后在onEnter时会触发updateChildrenPosition重新计算所有页面的坐标,也就是相当于已经滚动过了。 所以当update中开始滚动,结果就必然是错误的了。
解决办法:
在updateChildrenPosition中设置 _isAutoScrolling = false; 即重新计算页面坐标后,不再演示滚动动画,因为是初始化过程中没有动画也没问题。并且updateChildrenPosition只被调用了两处,另一处是在removePage中,也是没有滚动动画的。源码修改
修改函数后代码如下:
void PageView::updateChildrenPosition()
{
ssize_t pageCount = _pages.size();
_isAutoScrolling = false;
if (pageCount <= 0)
{
_curPageIdx = 0;
return;
}
if (_curPageIdx >= pageCount)
{
_curPageIdx = pageCount-1;
}
float pageWidth = getSize().width;
for (int i=0; i<pageCount; i++)
{
Layout* page = _pages.at(i);
page->setPosition(Point((i-_curPageIdx)*pageWidth, 0));
}
}
版本的不同对应的函数名字可能不同,笔者这里是:
void PageView::updateAllPagesPosition()
{
ssize_t pageCount = this->getPageCount();
_isAutoScrolling = false;
if (pageCount <= 0)
{
_curPageIdx = 0;
return;
}
if (_curPageIdx >= pageCount)
{
_curPageIdx = pageCount-1;
}
float pageWidth = getContentSize().width;
for (int i=0; i<pageCount; i++)
{
Layout* page = _pages.at(i);
page->setPosition(Vec2((i-_curPageIdx) * pageWidth, 0));
}
}
修改后都是添加了 _isAutoScrolling = false;
,再次编译,是不是就可以了呢?
The End
好了,今天的分享就到这里,如有不足之处,还望大家及时指正,随时欢迎探讨交流!!!