cocos2dx-3.0(23) ScrollView 缩放 及 touch新用法

来源:互联网 发布:评价李鸿章 知乎 编辑:程序博客网 时间:2024/05/16 19:24

转自 http://blog.csdn.net/ac_huang/article/details/37740515

ScrollView在3.0里面依然存在于extensions下面,看他的继承关系,他最终也是继承node,我们在来看看他的头文件应该怎么包含

1. 在头文件中添加 #include "cocos-ext.h" 在添加使用域  USING_NS_CC_EXT;

2. 添加多继承 public ScrollViewDelegate,如下:

[cpp] view plaincopy
  1. class HelloWorld : public LayerColor, public ScrollViewDelegate  

我们转到ScrollViewDelegate里面,发现这是一个抽象基类,里面有两个纯虚函数,我们需要自己实现

[cpp] view plaincopy
  1.     
  2. /** 
  3.     * @js NA 
  4.     * @lua NA 
  5.     */  
  6.    virtual void scrollViewDidScroll(ScrollView* view) = 0;  
  7.    /** 
  8.     * @js NA 
  9.     * @lua NA 
  10.     */  
  11.    virtual void scrollViewDidZoom(ScrollView* view) = 0;  

所以我们还需要在自己的类中添加这两行代码

[cpp] view plaincopy
  1. //scrollview滚动的时候会调用  
  2.     void scrollViewDidScroll(extension::ScrollView *view);  
  3.     //scrollview缩放的时候会调用  
  4.     void scrollViewDidZoom(extension::ScrollView *view);  

我们为了达到用鼠标点击图片移动时,出现缩小放大效果且能像相册一样切换图片,那么我们还需要添加触摸事件

[cpp] view plaincopy
  1.      
  2. bool onTouchBegan(Touch *touch, Event *pEvent);  
  3. void onTouchMoved(Touch *touch, Event *pEvent);  
  4. void onTouchEnded(Touch *touch, Event *pEvent);  

下面直接看看在头文件中代码

[cpp] view plaincopy
  1. #ifndef __HELLOWORLD_SCENE_H__  
  2. #define __HELLOWORLD_SCENE_H__  
  3.   
  4. #include "cocos2d.h"  
  5. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)  
  6. #include "extensions/cocos-ext.h"  
  7. #else  
  8. #include "cocos-ext.h"  
  9. #endif  
  10.   
  11. #include "cocostudio/CocoStudio.h"  
  12. #include "CocosGUI.h"  
  13.   
  14. USING_NS_CC;  
  15. USING_NS_CC_EXT;  
  16. using namespace cocostudio;  
  17. using namespace ui;  
  18.   
  19. #define PHOTO_COUNT 11  
  20.   
  21. class HelloWorld : public LayerColor, public ScrollViewDelegate  
  22. {  
  23. public:  
  24.     // there's no 'id' in cpp, so we recommend returning the class instance pointer  
  25.     static cocos2d::Scene* createScene();  
  26.   
  27.     // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone  
  28.     virtual bool init();    
  29.       
  30.     // a selector callback  
  31.     void menuCloseCallback(cocos2d::Ref* pSender);  
  32.       
  33.     bool onTouchBegan(Touch *touch, Event *pEvent);  
  34.     void onTouchMoved(Touch *touch, Event *pEvent);  
  35.     void onTouchEnded(Touch *touch, Event *pEvent);  
  36.   
  37.     //scrollview滚动的时候会调用  
  38.     void scrollViewDidScroll(extension::ScrollView *view);  
  39.     //scrollview缩放的时候会调用  
  40.     void scrollViewDidZoom(extension::ScrollView *view);  
  41.   
  42.     //添加触摸  
  43.     void addListener();  
  44.   
  45.     //添加粒子系统test  
  46.     void addParticle();  
  47.   
  48.     // implement the "static create()" method manually  
  49.     CREATE_FUNC(HelloWorld);  
  50.       
  51.     //骨骼动画  
  52.     Armature *m_armature;  
  53.   
  54.     //添加UI  
  55.     void addUI();  
  56.   
  57.     //添加场景(区别是我自定义的函数)  
  58.     void addMyScene();  
  59.   
  60. private:  
  61.     //根据手势滑动的距离和方向滚动图层  
  62.     void adjustScrollView(float offset);  
  63.   
  64.     //void setback();  
  65.   
  66. private:      
  67.     //存放所有图片  
  68.     Vector< Sprite* > m_spVec;  
  69.       
  70.     //当前的ScrollView  
  71.     extension::ScrollView *m_scrollView;  
  72.   
  73.     //touchBegen时的触摸位置  
  74.     Point m_touchPoint;  
  75.   
  76.     //当前是第几张图片  
  77.     int m_currentPage;  
  78.   
  79. };  
  80.   
  81. #endif // __HELLOWORLD_SCENE_H__  

上面有很多不是我们这相例子里面的,不过不影响

在来看看源文件里面

[cpp] view plaincopy
  1. bool HelloWorld::init()  
  2. {  
  3.     //////////////////////////////  
  4.     // 1. super init first  
  5.     if ( !LayerColor::initWithColor(Color4B(120,120,120,255)) )  
  6.     {  
  7.         return false;  
  8.     }  
  9.     auto visibleSize = Director::getInstance()->getVisibleSize();  
  10.     auto origin = Director::getInstance()->getVisibleOrigin();  
  11.   
  12.     /*auto scrollLayer = LayerColor::create(Color4B(0,0,0,255)); 
  13.     scrollLayer->setContentSize(Size(640,480)); 
  14.     scrollLayer->setAnchorPoint(Point(0.5,0.5)); 
  15.     scrollLayer->setPosition(Point(visibleSize.width/2-320, visibleSize.height/2-240)); 
  16.     this->addChild(scrollLayer);*/  
  17.   
  18.   
  19.     m_scrollView = extension::ScrollView::create(Size(visibleSize.width, visibleSize.height));  
  20.   
  21.     char spriteName[15];  
  22.     Layer *photoLayer = Layer::create();  
  23.     for(int i = 1; i <= PHOTO_COUNT; ++ i)  
  24.     {  
  25.         memset(spriteName, 0, sizeof(spriteName));  
  26.         sprintf(spriteName, "XJ%d.jpg", i);  
  27.         auto sprite = Sprite::create(spriteName);  
  28.         sprite->setPosition(Point(visibleSize.width*(i*1.0-0.5), visibleSize.height*0.5));  
  29.         //缩小一倍  
  30.         //sprite->setScale(0.5);  
  31.         photoLayer->addChild(sprite);  
  32.         m_spVec.pushBack(sprite);  
  33.     }  
  34.     //设置layer到滚动层容器中  
  35.     m_scrollView->setContainer(photoLayer);  
  36.     //重新设置滚动层的大小,这点很重要。  
  37.     m_scrollView->setContentSize(Size(PHOTO_COUNT*visibleSize.width, visibleSize.height));  
  38.     //设置代理  
  39.     m_scrollView->setDelegate(this);  
  40.     m_scrollView->setPosition(Point::ZERO);  
  41.     //我们只在水平方向上滚动。  
  42.     m_scrollView->setDirection(extension::ScrollView::Direction::HORIZONTAL);  
  43.     m_scrollView->setContentOffset(Point(0,0));  
  44.     this->addChild(m_scrollView);  
  45.   
  46.     m_currentPage = 0;  
  47.   
  48.     //注册触摸,和2.x的不同了, 还有一种lambda表达式的写法  
  49.     /************************************************************************ 
  50.     * lambda表达式的写法  
  51.     * auto listener1 = EventListenerTouchOneByOne::create(); 
  52.     * listener1->onTouchBegan = [](Touch* touch, Event* event) 
  53.     * { 
  54.     *   //dosomething 
  55.     * }; 
  56.     * listener1->onTouchMoved = [](Touch* touch, Event* event) 
  57.     * { 
  58.     *   //dosomething 
  59.     * }; 
  60.     * listener1->onTouchEnded = [=](Touch* touch, Event* event) 
  61.     * {  
  62.     *   //dosomething 
  63.     * }; 
  64.     * _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); 
  65.     ************************************************************************/  
  66.     auto listener = EventListenerTouchOneByOne::create();  
  67.     //设置是否想下传递触摸  
  68.     listener->setSwallowTouches(true);  
  69.     listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);  
  70.     listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);  
  71.     listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);  
  72.     //将触摸监听添加到eventDispacher中去  
  73.     _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);  
  74.     //注册触摸结束  
  75.   
  76.     return true;  
  77. }  
我们在也不需要使用setTouchEnabled这个函数了,你会发现你用了以后,编译时会提示这个函数已经被否决了,可以用setEnable();代替看看如何。

对于上面的触摸我们要重点说说,说完了在继续分析 ScrollView!!!

一、触摸监听listener的创建方式有两种,

1、EventListenerTouchOneByOne
2、EventListenerTouchAllAtOnce,
顾名思义EventListenerTouchOneByOne的意思单点触摸,EventListenerTouchAllAtOnce是多点触摸
而不需要再用设置Delegate的方式来做了。
3.0触摸机制还有个不同的地方,只要是放在最上面的那个精灵,那它的触摸优先级就最高。
我们用的按钮Menu 就是用这种方式设置触摸优先级的。

二、listener的调试优先级
我们进入addEventListenerWithSceneGraphPriority的定义中看一下,有下面这一行代码:

[cpp] view plaincopy
  1. listener->setFixedPriority(0);  


它将精灵的触摸优先级设置成0,从这里我们可以引申出两个问题,一个就是当我们要给精灵设置触摸优先级时,

因为0已经被“官府”征用了,另一个问题就是:如果自己想设置精灵的触摸优先级,那应该怎么做呢?
下面是提供的另外一种添加listener的方法:

[cpp] view plaincopy
  1. _eventDispatcher->addEventListenerWithFixedPriority(listener1 ,fixedPriority);  
在第二个参数里设置触摸优先级,这样就可以了。

三、多个目标如何添加触摸监听
如果多个精灵都想实现拖动的功能,那么这些精灵都可以使用listener1这一个触摸监听,
例如我们有三个精灵,sprite,sprite2,sprite3,他们调用listener1的方式:

[cpp] view plaincopy
  1.  _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, sprite1);  
  2.  _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite2);  
  3. _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite3);  

当我们进入到clone函数里面,你会发现他实现了复制触摸函数,也就是clone是专门用来这么干这的

[cpp] view plaincopy
  1. EventListenerTouchOneByOne* EventListenerTouchOneByOne::clone()  
  2. {  
  3.     auto ret = new EventListenerTouchOneByOne();  
  4.     if (ret && ret->init())  
  5.     {  
  6.         ret->autorelease();  
  7.           
  8.         ret->onTouchBegan = onTouchBegan;  
  9.         ret->onTouchMoved = onTouchMoved;  
  10.         ret->onTouchEnded = onTouchEnded;  
  11.         ret->onTouchCancelled = onTouchCancelled;  
  12.           
  13.         ret->_claimedTouches = _claimedTouches;  
  14.         ret->_needSwallow = _needSwallow;  
  15.     }  
  16.     else  
  17.     {  
  18.         CC_SAFE_DELETE(ret);  
  19.     }  
  20.     return ret;  
  21. }  

四、 删除触摸监听

如果想移除sprite的触摸移动,可以这么做:

[cpp] view plaincopy
  1. _eventDispatcher->removeEventListeners(EventListener::Type::TOUCH_ONE_BY_ONE);  

看完了新版 touch 我们继续看scrollView中的委托方法, 实现效果是对象在某个坐标范围内移动时会有缩放效果。

[cpp] view plaincopy
  1. void HelloWorld::scrollViewDidScroll(extension::ScrollView *view)  
  2. {  
  3.     //往右移动x坐标为正,往左移为负  
  4.     auto offPos = view->getContentOffset();  
  5.     //log("offset pos %f %f", offPos.x, offPos.y);  
  6.   
  7.     for(auto sp : m_spVec)  
  8.     {  
  9.         //获得当前对象的X坐标(不管怎么滚动,这个坐标都是不变的)  
  10.         auto pointX = sp->getPositionX();  
  11.           
  12.         //将精灵的 X坐标 + 偏移X坐标  
  13.         float offX = pointX + offPos.x;  
  14.   
  15.         //我的屏幕大小设置为了480*640,我的图片为320*480,我让图片居中显示(Point(160,320) ---> Point(320,320))  
  16.         //图片与图片之间的间隔是一个屏幕(480),我移动图片时不想让他缩放的太小,即只在80-240之间缩放,由于要对称,所以  
  17.         //相应的240-400也会产生和80-240之间相同的缩放比例,这样后一种需要用480-offx  
  18.         if( offX > 80 && offX <= 240 )  
  19.         {  
  20.             float scaleX = offX / 240;  
  21.             sp->setScale(scaleX);  
  22.             //log("scaleX1 %f",scaleX);  
  23.         }  
  24.         else if( offX > 240 && offX <= 400 )  
  25.         {  
  26.             float scaleX = (480 - offX) / 240;  
  27.             sp->setScale(scaleX);  
  28.             //log("scaleX2 %f",scaleX);  
  29.         }  
  30.         else  
  31.         {  
  32.             //对于在80-400之外的,我们保持同一缩放比例  
  33.             sp->setScale(80*1.0/240);  
  34.         }  
  35.     }  
  36. }  

我们应该知道,对象放到滚动层上,那么不管对象在scrollView上如何移动,它获得的坐标都是不会变的(如sp->getPosition()是不变的数值),这种情况下,如果我们想实现对象在某个坐标范围内会有缩放效果,那么只是去获取对象的坐标肯定是行不通的,所以肯定要找一个时刻在变化的”参照物”来利用下,该找什么呢?没错,就是scrollView的偏移坐标(scrollView->getContentOffset())!只要scrollView移动一下,那么它的 偏移量也随之改变。我这里就是利用对象的坐标与scrollView的偏移坐标之间不可告人的秘密,从而实现当前的目的。

实现了缩放,怎么去实现图片切换了,我们需要在触摸结束后,来切换ScrollView的偏移来达到切换效果

[cpp] view plaincopy
  1. bool HelloWorld::onTouchBegan(Touch *touch, Event *pEvent)  
  2. {  
  3.     m_touchPoint = Director::getInstance()->convertToGL(touch->getLocationInView());  
  4.     log("begin x = %f, y = %f", m_touchPoint.x, m_touchPoint.y);  
  5.   
  6.     return true;  
  7.   
  8. }  

获得一个触摸初始值,将来与结束值对比来做出一些约束

[cpp] view plaincopy
  1. void HelloWorld::onTouchEnded(Touch *touch, Event *pEvent)  
  2. {  
  3.     Point endPoint = Director::getInstance()->convertToGL(touch->getLocationInView());  
  4.     float distance = endPoint.x - m_touchPoint.x;  
  5.     log("end x = %f, y = %f", endPoint.x, endPoint.y);  
  6.   
  7.     //当我们移动的距离太小了,默认为不移动,也可以防止不小心碰了一下就切换了  
  8.     if( fabs(distance) <= 240 )  
  9.     {  
  10.         log("this is 1");  
  11.         adjustScrollView(0);  
  12.     }  
  13.     if(fabs(distance) > 240)  
  14.     {  
  15.         log("this is 2");  
  16.         adjustScrollView(distance);  
  17.     }  
  18. }  
去看看adjustScrollView里面实现了什么?

[cpp] view plaincopy
  1. <span style="font-family: Arial; font-size: 14px;">void HelloWorld::adjustScrollView(float offset)  
  2. {  
  3.     if( offset < 0 )  
  4.     {  
  5.         ++m_currentPage;  
  6.     }  
  7.     else if( offset > 0 )  
  8.     {  
  9.         --m_currentPage;  
  10.     }  
  11.   
  12.     if( m_currentPage < 0 )  
  13.     {  
  14.         m_currentPage = 0;  
  15.     }  
  16.       
  17.     else if( m_currentPage >= PHOTO_COUNT )  
  18.     {  
  19.         m_currentPage = 1;  
  20.     }  
  21.       
  22.     auto visibleSize = Director::getInstance()->getVisibleSize();  
  23.     //注意这里为 负的 visibleSize.width, 我开始写成正的,一直不出效果  
  24.     Point curPoint(-visibleSize.width * m_currentPage, 0);  
  25.     this->m_scrollView->setContentOffset(curPoint);  
  26.   
  27.     //setback  
  28.   
  29. }</span><span style="font-family:SimSun;font-size:14px;">  
  30. </span>  

此程序依然存在一些bug,比如一下子滑动了很远的距离,那样图片的切换就会变得杂乱无章,这个依然可以在 触摸结束里面使用 distance 来限制,留给以后去完善吧!!

下面看看效果:



0 0
原创粉丝点击