1、首先进入ScrollView.h文件中,在声明ScrollView这个类之前先声明一个委托类,之所以称之为委托,因为它的命名中包含 delegate 这个词。如下:
- class ScrollView; //前向声明scrollView
- class ScrollViewDelegate
- {
- public:
- virtual ~ScrollViewDelegate() {}
- virtual void scrollViewDidScroll(ScrollView* view) = 0;
- virtual void scrollViewDidZoom(ScrollView* view) = 0;
- };
可以看到在这个委托类的声明里有两个函数,一个是 scrollViewDidScroll() ,这是当scrollView在被拖动时会响应该函数;另一个是 scrollViewDidZoom ,我想应该是当scrollView在进行缩放时会响应该函数。
2、接下来就是声明ScrollView这个类了。 一开始我本能认为既然ScrollView要与ScrollViewDelegate这个类关联起来,那么它就应该继承ScrollViewDelegate吧?可实际上并没有, ScrollView只是单纯的继承一个Layer罢了 ,如下:
- class ScrollView : public Layer{};
发现有这么两个函数:
- ScrollViewDelegate* getDelegate() { return _delegate; }
- void setDelegate(ScrollViewDelegate* pDelegate) { _delegate = pDelegate; }
- ScrollViewDelegate* _delegate;
看到这里好像还不是很理解_delegate到底该怎么用,那起码有一些眉目了,毕竟ScrollViewDelegate已经浮出水面,不是那么神秘。继续往下看。
3、在ScrollView.cpp文件中,我开始找下_delegate这个成员变量都在哪里使用过。很快我在setContentOffset()这个函数中找到它的身影 。
- void ScrollView::setContentOffset(Point offset, bool animated/* = false*/)
- {
- if (animated)
- { //animate scrolling
- this->setContentOffsetInDuration(offset, BOUNCE_DURATION);
- }
- else
- {
- ... ...
- if (_delegate != NULL)
- {
- _delegate->scrollViewDidScroll(this); //就是这里。
- }
- }
- }
- _delegate->scrollViewDidScroll(this);
知道了上面这些,接下来就好办了, 我们只要知道在ScrollView中哪里有调用到setContentOffset() 这个函数就可以了 。
4、我往下搜索 setContentOffset 这个关键词,发现有在好几个地方调用到,其中最关键的还是在onTouchMoved()这个触摸回调函数中使用到。
onTouchMoved我也不用再多说了,每当我们触摸屏幕拖动时都会响应该函数,下面看下它的缩减版代码:
- void ScrollView::onTouchMoved(Touch* touch, Event* event)
- {
- if (!this->isVisible())
- {
- return;
- }
- if (std::find(_touches.begin(), _touches.end(), touch) != _touches.end())
- {
- if (_touches.size() == 1 && _dragging)
- { // scrolling
- ...
- if (frame.containsPoint(this->convertToWorldSpace(newPoint)))
- {
- ...
- this->setContentOffset(Point(newX, newY));//在这里调用到
- }
- }
- else if (_touches.size() == 2 && !_dragging)
- {
- const float len = _container->convertTouchToNodeSpace(_touches[0]).getDistance(
- _container->convertTouchToNodeSpace(_touches[1]));
- this->setZoomScale(this->getZoomScale()*len/_touchLength);//这里是调用与缩放相关的函数
- }
- }
- }
5、好了,说到这里一切都已经开始变得清晰了!现在我先整理下发型,然后简短的做个总结。
1) 首先在scrollView拖动过程中都会调用onTouchMoved()函数,然后再该函数中调用到到setContainOffset()这个函数,这个函数就是用来设置它的偏移位置的;
2) 在setContainOffset() 会调用到_delegate中的 scrollViewDidScroll()函数。
3) 为什么_delegate能有这么大的权力调用ScrollViewDelegate中的函数呢?原因就在于它是ScrollViewDelegate声明的,说直接点它就是ScrollViewDelegate的私生子!!!
6、下面我举个例子吧。
我先声明一个叫CoolStar的类,
- class CoolStar : public Layer,public ScrollViewDelegate
- {
- public:
- ...
- bool init();
- CREATE_FUNC(CoolStar);
- ...
- //scroll 委托
- void scrollViewDidScroll(MyScrollView* view);
- void scrollViewDidZoom(MyScrollView* view);
- }
- CoolStar为什么要继承ScrollViewDelegate呢?别急,往下看init()函数的定义。
- bool CoolStar::init()
- {
- auto scroll_layer = Layer::create();
- ...
- auto m_scroll = ScrollView::create(Size(...),scroll_layer);
- m_scroll->setDelegate(this);//看这里!!!
- return true;
- }
上面我创建了一个scrollView,然后设置scrollView的委托指向当前类的对象,也就是this(看注释的地方)
而我们知道setDelegate()这个函数是有参数的,它的参数是一个指向ScrollViewDelegate委托类的指针, 如下:
- void setDelegate(ScrollViewDelegate* pDelegate);
- m_scroll->setDelegate(this);
接下来定义两个scrollView的委托函数:
- void CoolStar::scrollViewDidScroll(MyScrollView* view)
- {
- CCLOG("star is so cool");
- }
- void CoolStar::scrollViewDidZoom(MyScrollView* view)
- {
- }
运行程序,发现每次拖动scrollView时控制台都会输出 "star is so cool" 这串字符串。
附:本文参加了CSDN博客大赛,亲如果觉得这篇文章不错,就大胆的来投上一票吧!!!http://vote.blog.csdn.net/Article/Details?articleid=34140469
尊重原创,转载请注明来源:http://blog.csdn.net/star530/article/details/34140469
//.h #include "cocos2d.h" #include <iostream> USING_NS_CC; class MyDelegate//自定义委托 { public: virtual void onGameStart() = 0; virtual void onGameEnd() = 0; }; class GameLayer : public Layer//定义游戏层 { public: static cocos2d::Scene* createScene();//单例,创建游戏层 virtual bool init(); CREATE_FUNC(GameLayer); CC_SYNTHESIZE(MyDelegate*, delegator,Delegator);//CC_SYNTHESIZE宏的使用 }; class StatusLayer : public cocos2d::Layer, public MyDelegate//定义状态层 { public: virtual bool init(); CREATE_FUNC(StatusLayer); void onGameStart() override;//重载两个方法 void onGameEnd() override; };
//.cpp #include "MyDelegate.h" //初始化状态层 bool StatusLayer::init() { return true; } void StatusLayer::onGameStart() { log("GameStart"); } void StatusLayer::onGameEnd() { log("GameEnd"); } //初始化游戏层 bool GameLayer::init() { StatusLayer* status_layer = StatusLayer::create();//创建状态层 this->setDelegator(status_layer); //通过游戏层的CC_SYNTHESIZE(MyDelegate*, delegator,Delegator);将delegator指向status_layer对象,delegator是MyDelegate类型指针,父类的指针或引用可以指向子类StatusLayer类型的子对象。通过多态的虚函数,动态联编执行status_layer的函数on_XXXX函数 this->delegator->onGameStart(); this->delegator->onGameEnd(); return true; }
在进入正题前我先简短的介绍下scrollView应该怎么用吧(想必大家也都会用~~):
1、记得在头文件里包含 “../extensions/cocos-ext.h",顺便声明下作用域:USING_NS_CC_EXT;
2、在类的继承里 加上ScrollViewDelegate,如下:
- class HelloWorld : public cocos2d::Layer,public ScrollViewDelegate
- void scrollViewDidScroll(ScrollView* view);
- void scrollViewDidZoom(ScrollView* view);
- void scrollViewMoveOver(ScrollView* view);
先看头文件:
- #ifndef __HELLOWORLD_SCENE_H__
- #define __HELLOWORLD_SCENE_H__
- #include "cocos2d.h"
- #include "../extensions/cocos-ext.h"
- USING_NS_CC;
- USING_NS_CC_EXT;
- class HelloWorld : public cocos2d::Layer,public ScrollViewDelegate
- {
- public:
- static cocos2d::Scene* createScene();//获取欢迎画面的Scene
- virtual bool init();
- void menuCloseCallback(Ref* pSender);
- CREATE_FUNC(HelloWorld);
- //scroll 委托
- void scrollViewDidScroll(ScrollView* view);
- void scrollViewDidZoom(ScrollView* view);
- void scrollViewMoveOver(ScrollView* view);
- private:
- Vector<Sprite*> sp_vec;//声明一个容器
- };
- #endif // __HELLOWORLD_SCENE_H__
下面看定义
- bool HelloWorld::init()
- {
- //首先创建scrollView
- auto scroll_layer = Layer::create();//创建scrollView中的容器层
- scroll_layer->setPosition(Point::ZERO);
- scroll_layer->setAnchorPoint(Point::ZERO);
- scroll_layer->setContentSize(Size(600,300));//设置容器层大小为(600,300)
- auto scrollView = ScrollView::create(Size(400,300),scroll_layer);//创建scrollView,显示窗口大小为(400,300)
- scrollView->setDelegate(this);//设置委托
- scrollView->setDirection(ScrollView::Direction::HORIZONTAL);//设置滚动方向为水平
- scrollView->setPosition(Point(300,200));
- this->addChild(scrollView,2);
- //创建三个对象
- auto boy = Sprite::create("boy.png");//没错,主角又是我们熟悉的那仨。多么温馨。
- boy->setPosition(Point(150,100));
- scroll_layer->addChild(boy,2);
- auto girl = Sprite::create("girl_1.png");
- girl->setPosition(Point(300,100));
- scroll_layer->addChild(girl,2);
- auto girl3 = Sprite::create("girl_3.png");
- girl3->setPosition(Point(450,100));
- scroll_layer->addChild(girl3,2);
- sp_vec.pushBack(boy);//将三个对象放入容器中
- sp_vec.pushBack(girl);
- sp_vec.pushBack(girl3);
- return true;
- }
接下来看下scrollView的委托函数,这里只要看scrollViewDidScroll 就好了。实现效果是对象在某个坐标范围内移动时会有缩放效果。
- void HelloWorld::scrollViewDidScroll(ScrollView* view)
- {
- //在scrollView拖动时响应该函数
- auto offsetPosX = (view->getContentOffset()).x;//获得偏移X坐标(向右移动,偏移量为正数,向左则为负数)
- // CCLOG("offset pos is %f , %f",offsetPos.x,offsetPos.y);
- //for 循环遍历容器中的每个精灵
- for( auto e : sp_vec )
- {
- auto pointX = e->getPositionX();//获得当前对象的X坐标(不管怎么滚动,这个坐标都是不变的)
- float endPosX = pointX + offsetPosX;//将精灵的 X坐标 + 偏移X坐标
- //当endPosX在 150~250 范围,则对象的大小从左向右递增
- if( endPosX > 150 && endPosX < 250 )
- {
- float x = endPosX / 150;//放大倍数为 endPosX / 150;
- e->setScale(x);
- CCLOG("x = %f",x);
- }
- //当endPosX在 250~350 范围,则对象的大小从左向右递减
- else if( endPosX > 250 && endPosX < 350 )
- {
- //下面这个公式不好解释,我就这么说吧:
- //假设 endPosX = 200,那么放大倍数应该是 200 / 150 = 1.33左右,那么当endPosX = 300时,出于对称的原理,我们以250为对称中心,那么
- //300 的放大倍数也应该是 1.33。这就是下面的公式由来
- float a = endPosX - 250;
- float b = 250 - a;
- float x = b / 150;
- e->setScale(x);
- }
- else
- {
- //不是在上面的范围,则设置为正常大小
- e->setScale(1.0f);
- }
- }
- }
- void HelloWorld::scrollViewDidZoom(ScrollView* view)
- {
- //do something
- }
- void HelloWorld::scrollViewMoveOver(ScrollView* view)
- {
- //do something
- }
恩,注释写的很清楚啦,但我还是要稍微补充一些东东:我们应该知道,对象放到滚动层上(如scroll_layer->addChild(boy)),那么不管对象在scrollView上如何移动,它获得的坐标都是不会变的(如boy->getPosition()是不变的数值),这种情况下,如果我们想实现对象在某个坐标范围内会有缩放效果,那么只是去获取对象的坐标肯定是行不通的,所以肯定要找一个时刻在变化的”参照物”来利用下,该找什么呢?没错,就是scrollView的偏移坐标(scrollView->getContentOffset())!只要scrollView移动一下,那么它的 偏移量也随之改变。我这里就是利用对象的坐标与scrollView的偏移坐标之间不可告人的秘密,从而实现当前的目的。
下面看下运行效果。
另一个代码示例
//.h #include "cocos2d.h" #include "../extensions/cocos-ext.h" USING_NS_CC_EXT; USING_NS_CC; class CCScrollView : public cocos2d::Layer,public ScrollViewDelegate { public: CREATE_FUNC(CCScrollView); virtual bool init(); static cocos2d::Scene* createScene(); void scrollViewDidScroll(ScrollView* view); void scrollViewDidZoom(ScrollView* view); void scrollViewMoveOver(ScrollView* view); void menuCloseCallback(Ref* pSender); private: Vector<Sprite*> sp_vec; };
//.cpp #include "CCScrollView.h" Scene* CCScrollView::createScene() { // 'scene' is an autorelease object auto scene = Scene::create(); // 'layer' is an autorelease object auto layer = CCScrollView::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } bool CCScrollView::init() { bool bRet = false; do{ CC_BREAK_IF(!Layer::init()); //首先创建scrollView的容器 auto scroll_layer = Layer::create(); scroll_layer->setPosition(Vec2::ZERO); scroll_layer->setAnchorPoint(Vec2::ZERO); scroll_layer->setContentSize(Size(600,300));//设置容器的大小 scroll_layer->setColor(Color3B::WHITE); auto scrollView = ScrollView::create(Size(400,300),scroll_layer); scrollView->setDelegate(this);//设置委托 scrollView->setDirection(ScrollView::Direction::HORIZONTAL); scrollView->setPosition(Vec2(300, 200)); this->addChild(scrollView,2); //创建三个对象 auto boy = Sprite::create("boy.png"); boy->setPosition(Vec2(150, 100)); scroll_layer->addChild(boy); auto girl1 = Sprite::create("girl1.png"); girl1->setPosition(Vec2(300, 100)); scroll_layer->addChild(girl1); auto girl2 = Sprite::create("girl2.png"); girl2->setPosition(Vec2(450, 100)); scroll_layer->addChild(girl2); sp_vec.pushBack(boy); sp_vec.pushBack(girl1); sp_vec.pushBack(girl2); bRet = true; }while(0); return bRet; } void CCScrollView::scrollViewDidScroll(ScrollView* view) { //在ScrollView拖动时响应该函数 auto offsetPosX = (view->getContentOffset()).x;//获得偏移X坐标,向右为正,向左为负 auto offsetPos = view->getContentOffset(); log("offset pos is %f,%f",offsetPos.x,offsetPos.y); for(auto e : sp_vec) { auto pointX = e->getPositionX();//获得当前对象的X坐标(不管怎么滚动,这个坐标都是不变的) float endPosX = pointX + offsetPosX;//将精灵X坐标+偏移X坐标 //当endPosX在150~250范围,则对象的大小从左向右递增 if(endPosX > 150 && endPosX < 250) { float x = endPosX / 150;//放大的倍数x e->setScale(x); log("x=%f",x); } //当endPosX在250~350之间,则对象的大小从左向右递减 else if(endPosX > 250 && endPosX < 350) { float a = endPosX - 250; float b = 250 - a; float x = b/150; e->setScale(x); } else { e->setScale(1.0f); } } } void CCScrollView::scrollViewDidZoom(cocos2d::extension::ScrollView *view) { log("scrollViewDidZoom"); } void CCScrollView::scrollViewMoveOver(cocos2d::extension::ScrollView *view) { log("scrollViewMoveOver"); }