cocos2dx lua PageView循环滑动及pageView的scrollToPage()偏移问题

16 篇文章 0 订阅
11 篇文章 0 订阅


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

  好了,今天的分享就到这里,如有不足之处,还望大家及时指正,随时欢迎探讨交流!!!

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

对酒当歌﹏✍

您的鼓励是我写作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值