• 生命游戏(两),有一种东西叫CCScrollView


    订婚app要么game'肯定不会陌生:CCScrollView并且CCTableView。

    假如我不知道是什么CCScrollView,再看看testcpp要么testlua样品棒。

    先说说CCScrollView:.cpp和.h文件在cocos2d-x-2.2.3extensionsGUI文件夹下,所以要使用CCScrollView首先要引入头文件#include "cocos-ext.h",以及空间名USING_NS_CC_EXT; 假设头文件报错,那么请检查project的配置。这里就不多说了无非是引用,link的输入和c/c++里面的附加包括文件夹。(ps:假设是个简单的project个人建议你又一次建个新project)。

    #include "cocos-ext.h"
    USING_NS_CC_EXT;
    void HelloWorld::initScroll(){
      CCSize size = CCDirector::sharedDirector()->getWinSize();
      CCScrollView* scroll = CCScrollView::create(size); //scroll->setViewSize(size);
      scroll->setDirection(kCCScrollViewDirectionVertical);  //设置滚动方向
      CCSprite* p1 = CCSprite::create("HelloWorld.png");
      p1->ignoreAnchorPointForPosition(true);
      CCSprite* p2 = CCSprite::create("HelloWorld.png");
      p2->setPosition(ccp(0,size.height));
      p2->ignoreAnchorPointForPosition(true);
      CCLayer* conLayer = CCLayer::create();
      conLayer->addChild(p1);
      conLayer->addChild(p2);
      scroll->setContainer(conLayer);
      scroll->setContentSize(CCSizeMake(size.width,320*2));
      this->addChild(scroll);
    }
    
    执行以上代码。我们就能够使用Scrollview了,总结起来就是

    1、create scrollview 

    2、create Container层

    3、将内容add到containner层

    4、container层add到scrollview,scrollview增加指定layer或者scene


    这里有几个点须要注意:

    1、setContentSize不是scrollview大小,而是可滑动面的大小,viewSize是滑动面大小。

    2、setContentSize不能放在setContainer前面,假设放在前面。那么这个滑动后回自己主动回到第一页。

    3、container层中的内容position须要手动设定

    4、假设不create container层,那么CCScrollView会给你创建个一个,可是直接在scrollview下add多个node仅仅会保留一个,原因在后面分析。



    好了。最easy的部分写完了,以下分析一下CCScrollView的源代码和一些让人蛋疼的东西。

    CCScrollView::CCScrollView()
    : m_fZoomScale(0.0f)
    , m_fMinZoomScale(0.0f)
    , m_fMaxZoomScale(0.0f)
    , m_pDelegate(NULL)			   //view管理器
    , m_eDirection(kCCScrollViewDirectionBoth)  //滚动类型
    , m_bDragging(false)
    , m_pContainer(NULL)                        //container层
    , m_bTouchMoved(false)
    , m_bBounceable(false)                      //弹性开关
    , m_bClippingToBounds(false)                //弹性开关
    , m_fTouchLength(0.0f)
    , m_pTouches(NULL)
    , m_fMinScale(0.0f)
    , m_fMaxScale(0.0f)
    {
    
    }
    CCScrollView* CCScrollView::create(CCSize size, CCNode* container/* = NULL*/)
    {
        CCScrollView* pRet = new CCScrollView();
        if (pRet && pRet->initWithViewSize(size, container))
        {
            pRet->autorelease();
        }
        else
        {
            CC_SAFE_DELETE(pRet);
        }
        return pRet;
    }
    bool CCScrollView::initWithViewSize(CCSize size, CCNode *container/* = NULL*/)
    {
        if (CCLayer::init())//scrollview继承CCLayer
        {
            m_pContainer = container;
            
            if (!this->m_pContainer)
            {
            m_pContainer = CCLayer::create();//create container层
                this->m_pContainer->ignoreAnchorPointForPosition(false);
                this->m_pContainer->setAnchorPoint(ccp(0.0f, 0.0f));
            }
    
            this->setViewSize(size);//设置视图尺寸,假设使用CCScrollView::create() 引擎会设置 size 为 CCSizeMake(200, 200)
    
    
            setTouchEnabled(true); //scrollview 自然是要有触摸的
            m_pTouches = new CCArray();//触点 array
            m_pDelegate = NULL;
            m_bBounceable = true;    //默认弹性开启
            m_bClippingToBounds = true;
            //m_pContainer->setContentSize(CCSizeZero);
            m_eDirection  = kCCScrollViewDirectionBoth;  //默认both滚动模式
            m_pContainer->setPosition(ccp(0.0f, 0.0f));
            m_fTouchLength = 0.0f;
            
            this->addChild(m_pContainer);
            m_fMinScale = m_fMaxScale = 1.0f;  //默认放缩1.0
            m_mapScriptHandler.clear();  //清理回调列表
            return true;
        }
        return false;
    }

    我们补习一下滚动模式,CCScrollView中定义了一个枚举类型来表示滚动模式

    typedef enum {
        kCCScrollViewDirectionNone = -1,   //不滚动
        kCCScrollViewDirectionHorizontal = 0,  //横向滚动
        kCCScrollViewDirectionVertical,   //竖向滚动
        kCCScrollViewDirectionBoth   //双向滚动
    } CCScrollViewDirection;
    

    初始化就先讲到这,在看看属性设置上一些东西

    void CCScrollView::setContainer(CCNode * pContainer)
    {
        // Make sure that 'm_pContainer' has a non-NULL value since there are
        // lots of logic that use 'm_pContainer'.
        if (NULL == pContainer)
            return;
        this->removeAllChildrenWithCleanup(true);  //这个就是多次使用setContainer却仅仅能显示一个sprite的原因
        this->m_pContainer = pContainer //假设setContentSize在setContainer之前,实际上设置的size给引擎自带的container,而不是目标container
        this->m_pContainer->ignoreAnchorPointForPosition(false);
        this->m_pContainer->setAnchorPoint(ccp(0.0f, 0.0f));
        this->addChild(this->m_pContainer);
        this->setViewSize(this->m_tViewSize);
    }
    
    void CCScrollView::setViewSize(CCSize size)
    {
        m_tViewSize = size;
        CCLayer::setContentSize(size); //这里不是container的size。而是scrollview的size
    }
    void CCScrollView::setContentSize(const CCSize & size)
    {
        if (this->getContainer() != NULL)
        {
            this->getContainer()->setContentSize(size); //这里才是设置container的size
            this->updateInset();                        //设置弹性状态下虚拟边界值。
        }
    }
    
    /**尽管setcontainer仅仅能用一次。可是addchild却能够满足我们的愿望。

     * make sure all children go to the container  */ void CCScrollView::addChild(CCNode * child, int zOrder, int tag) {     child->ignoreAnchorPointForPosition(false);     child->setAnchorPoint(ccp(0.0f, 0.0f));     if (m_pContainer != child) {         m_pContainer->addChild(child, zOrder, tag);     } else {         CCLayer::addChild(child, zOrder, tag);     } }

    属性设置完了,显示是没问题了。只是scrollview究竟是怎样滚动的?

    首先:
        virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
        virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
        virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
    

    代码太多,这里就不全贴出来了。有兴趣能够自己去看源代码。让我们把主要代码摘出来

    void CCScrollView::setContentOffset(CCPoint offset, bool animated/* = false*/) //此方法在ccTouchMoved中调用,功能是将container移动到指定位置
    {
        if (animated)
        { //animate scrolling
            this->setContentOffsetInDuration(offset, BOUNCE_DURATION);//将container移动到指定位置,BOUNCE_DURATION=0.15f。移动时间
        } 
        else
        { //set the container position directly
            if (!m_bBounceable)             //未开启边界弹性
            {
                const CCPoint minOffset = this->minContainerOffset();
                const CCPoint maxOffset = this->maxContainerOffset();
                
                offset.x = MAX(minOffset.x, MIN(maxOffset.x, offset.x));
                offset.y = MAX(minOffset.y, MIN(maxOffset.y, offset.y));
            }
    
    
            m_pContainer->setPosition(offset);
    
    
            if (m_pDelegate != NULL)
            {
                m_pDelegate->scrollViewDidScroll(this);   //回调虚函数,由子类override。lua中能够注冊该函数的回调
            }
        }
    }
    
    void CCScrollView::setContentOffsetInDuration(CCPoint offset, float dt) //好了,动画来了。
    {
        CCFiniteTimeAction *scroll, *expire;
        scroll = CCMoveTo::create(dt, offset);
        expire = CCCallFuncN::create(this, callfuncN_selector(CCScrollView::stoppedAnimatedScroll));
        m_pContainer->runAction(CCSequence::create(scroll, expire, NULL));
        this->schedule(schedule_selector(CCScrollView::performedAnimatedScroll));  
    }
    
    void CCScrollView::deaccelerateScrolling(float dt) //该方法是在ccTouchEnded中的主要内容 ,当滑动停止的时候,container还会移动一段距离。
        if (m_bDragging) //拖拽的话该方法不会多次循环调用
        {
            this->unschedule(schedule_selector(CCScrollView::deaccelerateScrolling));
            return;
        }
        float newX, newY;
        CCPoint maxInset, minInset;
        
        m_pContainer->setPosition(ccpAdd(m_pContainer->getPosition(), m_tScrollDistance));
        
        if (m_bBounceable)            //弹性设置边界,该值>设置containerSize值。
        {
            maxInset = m_fMaxInset;
            minInset = m_fMinInset;
        }
        else
        {
            maxInset = this->maxContainerOffset();
            minInset = this->minContainerOffset();
        }
        
        //check to see if offset lies within the inset bounds
        newX     = MIN(m_pContainer->getPosition().x, maxInset.x);
        newX     = MAX(newX, minInset.x);
        newY     = MIN(m_pContainer->getPosition().y, maxInset.y);
        newY     = MAX(newY, minInset.y);
        
        newX = m_pContainer->getPosition().x;
        newY = m_pContainer->getPosition().y;
        
        m_tScrollDistance     = ccpSub(m_tScrollDistance, ccp(newX - m_pContainer->getPosition().x, newY - m_pContainer->getPosition().y));
        m_tScrollDistance     = ccpMult(m_tScrollDistance, SCROLL_DEACCEL_RATE);
        this->setContentOffset(ccp(newX,newY));
        //滑动灵敏度推断,边界推断,结束定时任务。在每次移动结束都会调用
        if ((fabsf(m_tScrollDistance.x) <= SCROLL_DEACCEL_DIST &&
             fabsf(m_tScrollDistance.y) <= SCROLL_DEACCEL_DIST) ||
            newY > maxInset.y || newY < minInset.y ||
            newX > maxInset.x || newX < minInset.x ||
            newX == maxInset.x || newX == minInset.x ||
            newY == maxInset.y || newY == minInset.y)
        {
            this->unschedule(schedule_selector(CCScrollView::deaccelerateScrolling));
            this->relocateContainer(true); //又一次设置locate。边界弹性
        }
    }
    
    void CCScrollView::relocateContainer(bool animated)  //没什么好说的,推断边界。如超出的话就移动回来
    {
        CCPoint oldPoint, min, max;
        float newX, newY;
        
        min = this->minContainerOffset();
        max = this->maxContainerOffset();
        
        oldPoint = m_pContainer->getPosition();
    
    
        newX     = oldPoint.x;
        newY     = oldPoint.y;
        if (m_eDirection == kCCScrollViewDirectionBoth || m_eDirection == kCCScrollViewDirectionHorizontal)
        {
            newX     = MAX(newX, min.x);
            newX     = MIN(newX, max.x);
        }
    
    
        if (m_eDirection == kCCScrollViewDirectionBoth || m_eDirection == kCCScrollViewDirectionVertical)
        {
            newY     = MIN(newY, max.y);
            newY     = MAX(newY, min.y);
        }
    
    
        if (newY != oldPoint.y || newX != oldPoint.x)
        {
           this->setContentOffset(ccp(newX, newY), animated);
        }
    }
    
    好了。到这里基本上大体就算一站了,还有些细节以及放缩就不在细说。

    那么我们搞了这么久到底是要干什么?非常easy。我们希望滑动能够自己主动匹配图片的大小。

    怎么做那?

    方法一:改动源代码:

    1、加入一个成员变量。记录初始滑动位置。

    2、加入成员变量:图片匹配size

    3、改动relocateContainer,max。min取近期符合要求的坐标。setContentOffset or setContentOffsetInDuration就可以

    原理就是:

    1. touch開始的时候记录初始位置。在touch结束的时候, 获取结束时刻的时间和位置. 通过滑动距离等推断是否已经翻页,然后设置终于目标坐标。
      • 方法二:自己定义控件,继承CCScrollView。在ccTouchEnded末尾调用自己定义函数,实现以上功能。


      • 假设是lua开发,我推荐第一中方法,高速,改动相对少些。不用新增pkg文件。假设是c++的话,还是继承的好。
      • 试着改动下源代码,不仅有利于学习,并且有快感!!!












    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    ros 使用命令测试topic
    python unicode
    python ros 回充demo
    python ros 回充调用demo
    flask报错No module named 'flask.ext'
    python flask 接口
    ros 安装c++编译的可执行文件
    Linux-Ubuntu14.04下mongodb安装部署
    如何在Ubuntu 14.04中安装最新版Eclipse
    ubuntu 14.04 安装redis5.0.3
  • 原文地址:https://www.cnblogs.com/yxwkf/p/4627192.html
Copyright © 2020-2023  润新知