cocos2d-x CCScrollView 源代码分析

版本号源代码来自2.x,转载请注明

另我实现了能够循环的版本号http://blog.csdn.net/u011225840/article/details/31354703

1.继承树结构

能够看出,CCScrollView本质是CCLayer的一种,具备层的一切属性和方法。关于CCLayer的源代码分析,兴许会有。

2.重要的成员

 1.  CCScrollViewDelegate* m_pDelegate;

cocos2d-x中,运用了非常多delegate这样的模式。

以下简单的说明下delegate这样的模式。

(至于delegate与proxy的差别,请先參考下headfirst中的proxy三种情况,然后能够google差别。这里不再赘述。)

XXXDelegate中封装了接口(c++中的实现就是虚函数与必须实现的纯虚函数),类A中存在某些方法,比方说View中的getDataNum,View会依据数据的多少来确定界面的显示方式。可是A与数据并没有直接的关联。于是乎。在View A中的getDataNum会调用A内部的DataDelegate的方法来获取数据的多少。至于这个数据详细是什么,仅仅须要实现一个DataDelegate的详细类就可以。这样。View与数据的耦合度就很低。View仅仅依赖抽象的DataDelegate。

在兴许的源代码分析中。能够看出delegate的妙处。

3.源代码解析

3.1 ccTouchBegan

对于ccTouchBegan中的重要部分,我加入了凝视。能够通过代码看出。CCscrollView支持单点和双点触摸。

bool CCScrollView::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
    if (!this->isVisible() || !m_bCanTouch)
    {
        return false;
    }

    CCRect frame = getViewRect();

    //dispatcher does not know about clipping. reject touches outside visible bounds.
	/*
	1. ccScrollView仅仅同意至多两个触摸点。多于两个后将不会觉得发成了触摸。
	2. 当CCScrollView处于移动状态时,在此状态下新发生触摸将不会被觉得发生。
	3.注意frame不是当前的尺寸。而是当前ViewSize的frame,也就是触摸点必须在显示的Rect内才会认定为触摸(能够通过setViewSize来设置大小)
   */
    if (m_pTouches->count() > 2 ||
        m_bTouchMoved          ||
        !frame.containsPoint(m_pContainer->convertToWorldSpace(m_pContainer->convertTouchToNodeSpace(touch))))
    {
		m_pTouches->removeAllObjects();
        return false;
    }

    if (!m_pTouches->containsObject(touch))
    {
        m_pTouches->addObject(touch);
	}
	//CCLOG("CCScrollView::ccTouchBegan %d", m_pTouches->count());
	/*
	 当触摸点为1的时候,设置单点触摸的属性。

尤其是m_bDragging属性表示触摸行为是拖动
   */
    if (m_pTouches->count() == 1)
    { // scrolling
        m_tTouchPoint     = this->convertTouchToNodeSpace(touch);
        m_bTouchMoved     = false;
        m_bDragging     = true; //dragging started
        m_tScrollDistance = ccp(0.0f, 0.0f);
        m_fTouchLength    = 0.0f;
    }
	/*
		当触摸点个数为2时,设置双点触摸的属性
	*/
    else if (m_pTouches->count() == 2)
    {
        m_tTouchPoint  = ccpMidpoint(this->convertTouchToNodeSpace((CCTouch*)m_pTouches->objectAtIndex(0)),
                                   this->convertTouchToNodeSpace((CCTouch*)m_pTouches->objectAtIndex(1)));
        m_fTouchLength = ccpDistance(m_pContainer->convertTouchToNodeSpace((CCTouch*)m_pTouches->objectAtIndex(0)),
                                   m_pContainer->convertTouchToNodeSpace((CCTouch*)m_pTouches->objectAtIndex(1)));
        m_bDragging  = false;
    }
    return true;
}

3.2 ccTouchMoved

同上。代码里面加了凝视

void CCScrollView::ccTouchMoved(CCTouch* touch, CCEvent* event)
{

    if (!this->isVisible())
    {
        return;
    }

	/*
		假设此时不同意滚动,则退出。

这个能够通过set函数设置。默觉得false
	*/
	if(this->m_bScrollLock)
	{
		return;
	}

    if (m_pTouches->containsObject(touch))
    {
		/*
			啊哦,好玩的来咯。

滚动状态时
		*/
        if (m_pTouches->count() == 1 && m_bDragging)
        { // scrolling
            CCPoint moveDistance, newPoint, maxInset, minInset;
            CCRect  frame;
            float newX, newY;

            frame = getViewRect();
			//获得当前点的坐标,而且获得当前点与上一次触碰点的距离(moveDistance也是CCPoint,x与y是当前点与上一点的x距离,y距离)
            newPoint     = this->convertTouchToNodeSpace((CCTouch*)m_pTouches->objectAtIndex(0));
            moveDistance = ccpSub(newPoint, m_tTouchPoint);

            float dis = 0.0f;
			//假设有方向的限定,依据方向限定获取相应的距离
            if (m_eDirection == kCCScrollViewDirectionVertical)
            {
                dis = moveDistance.y;
            }
            else if (m_eDirection == kCCScrollViewDirectionHorizontal)
            {
                dis = moveDistance.x;
            }
            else
            {
                dis = sqrtf(moveDistance.x*moveDistance.x + moveDistance.y*moveDistance.y);
            }

			//假设移动距离过短,则不推断发生了移动
            if (!m_bTouchMoved && fabs(convertDistanceFromPointToInch(dis)) < MOVE_INCH )
            {
                //CCLOG("Invalid movement, distance = [%f, %f], disInch = %f", moveDistance.x, moveDistance.y);
                return;
            }
            //第一次移动。则将moveDistance置0
            if (!m_bTouchMoved)
            {
                moveDistance = CCPointZero;
            }

            m_tTouchPoint = newPoint;
            m_bTouchMoved = true;
            //点必须在viewRect内部
            if (frame.containsPoint(this->convertToWorldSpace(newPoint)))
            {
				//依据能够移动的direction来设置moveDistance
                switch (m_eDirection)
                {
                    case kCCScrollViewDirectionVertical:
                        moveDistance = ccp(0.0f, moveDistance.y);
                        break;
                    case kCCScrollViewDirectionHorizontal:
                        moveDistance = ccp(moveDistance.x, 0.0f);
                        break;
                    default:
                        break;
                }
                //这个版本号无用啊。。

。。

maxInset = m_fMaxInset;
                minInset = m_fMinInset;

				//获取容器的新坐标,注意是容器哦
                newX     = m_pContainer->getPosition().x + moveDistance.x;
                newY     = m_pContainer->getPosition().y + moveDistance.y;
				//滚动的CCPoint矢量设置
                m_tScrollDistance = moveDistance;
                this->setContentOffset(ccp(newX, newY));
            }
        }
		//双点触摸时。效果是缩放。len是双点触摸每次移动时的距离。
		//而m_fTouchLength是双点開始时的距离,会依据move过程中距离与初始距离的比例进行缩放
        else if (m_pTouches->count() == 2 && !m_bDragging)
        {
            const float len = ccpDistance(m_pContainer->convertTouchToNodeSpace((CCTouch*)m_pTouches->objectAtIndex(0)),
                                            m_pContainer->convertTouchToNodeSpace((CCTouch*)m_pTouches->objectAtIndex(1)));
            this->setZoomScale(this->getZoomScale()*len/m_fTouchLength);
        }
    }
}

单点触摸时。调用了一个函数叫setContentOffset。以下继续分析contentOffset。

setContentOffset

void CCScrollView::setContentOffset(CCPoint offset, bool animated/* = false*/)
{
	//默认情况,不做处理
    if (animated)
    { //animate scrolling
        this->setContentOffsetInDuration(offset, BOUNCE_DURATION);
    }
	//好玩的东西哦
    else
    { //set the container position directly
		//是否做越界处理。什么是越界。就是当你拖动整个容器时,假设已经到了容器的边界。还能不能再拖动,能够通过set函数进行设置
        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));

        }
		//CCLOG("The offset x is %f , y is %f",offset.x,offset.y);
        m_pContainer->setPosition(offset);

		//伟大的delegate来了。当你在滚动过程中想做除了基本界面滚动的额外操作时,请依据自己的不同情况。实现该delegate~完美的依赖抽象的设计。nice
        if (m_pDelegate != NULL)
        {
            m_pDelegate->scrollViewDidScroll(this);
        }
    }
}

双点触摸时,调用了一个重要的函数setZoomScale。

setZoomScale

void CCScrollView::setZoomScale(float s)
{
    if (m_pContainer->getScale() != s)
    {
        CCPoint oldCenter, newCenter;
        CCPoint center;
        //设置缩放中心
        if (m_fTouchLength == 0.0f)
        {
            center = ccp(m_tViewSize.width*0.5f, m_tViewSize.height*0.5f);
            center = this->convertToWorldSpace(center);
        }
        else
        {
            center = m_tTouchPoint;
        }
        //缩放后中心的位置相对于world坐标系会产生offset,这里将offset进行计算
        oldCenter = m_pContainer->convertToNodeSpace(center);
        m_pContainer->setScale(MAX(m_fMinScale, MIN(m_fMaxScale, s)));
        newCenter = m_pContainer->convertToWorldSpace(oldCenter);

        const CCPoint offset = ccpSub(center, newCenter);
		//delegate的重新出现
        if (m_pDelegate != NULL)
        {
            m_pDelegate->scrollViewDidZoom(this);
        }
		//将产生的offset进行处理
        this->setContentOffset(ccpAdd(m_pContainer->getPosition(),offset));
    }
}

3.3 ccTouchEnded

能坚持看到这里,已经能够看见胜利的曙光了。曙光。。

void CCScrollView::ccTouchEnded(CCTouch* touch, CCEvent* event)
{
    if (!this->isVisible())
    {
        return;
    }
	//将touch从pTouches中移除
    if (m_pTouches->containsObject(touch))
    {
		//当剩下一个touch时。须要在每一帧调用方法deaccelerateScrolling
        if (m_pTouches->count() == 1 && m_bTouchMoved)
        {
            this->schedule(schedule_selector(CCScrollView::deaccelerateScrolling));
        }
		m_pTouches->removeObject(touch);
		//CCLOG("CCScrollView::ccTouchEnded %d", m_pTouches->count());
		//m_pDelegate->scrollViewDidStop(this);
    }
	//没有touch时,须要设置状态
    if (m_pTouches->count() == 0)
    {
        m_bDragging = false;
        m_bTouchMoved = false;
    }
}

deaccelerateScrolling

在这个函数中,有一个地方没有理解。求大神指点

void CCScrollView::deaccelerateScrolling(float dt)
{
	//假设刚好在帧開始前 又有一个触摸点发生了began。造成了滚动状态,则取消并返回
    if (m_bDragging)
    {
        this->unschedule(schedule_selector(CCScrollView::deaccelerateScrolling));
        return;
    }

	//好玩的东西来咯

    float newX, newY;
    CCPoint maxInset, minInset;
    CCLOG("The end distance is %f",m_tScrollDistance.x);
	//这里我不清楚为啥要出来,我用输出发如今move中。已经将此offset设置过了,不知为何还要设置,求大神解答。
    m_pContainer->setPosition(ccpAdd(m_pContainer->getPosition(), m_tScrollDistance));

	//是否同意越界,获得的inset信息
    if (m_bBounceable)
    {
        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);
		//伟大的delegate。。。
		m_pDelegate->scrollViewDidStop(this);

    }
}

relocateContainer

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);
    }
	//还是调用setContentOffset,可是须要动画
    if (newY != oldPoint.y || newX != oldPoint.x)
    {
        this->setContentOffset(ccp(newX, newY), animated);
    }
}

setContentOffsetInDuration

void CCScrollView::setContentOffsetInDuration(CCPoint offset, float dt)
{
    CCFiniteTimeAction *scroll, *expire;
    //滚动的偏移动画
    scroll = CCMoveTo::create(dt, offset);
	//滚动完毕后的动画(负责停止performedAnimatedScroll。而且调用delegate)
    expire = CCCallFuncN::create(this, callfuncN_selector(CCScrollView::stoppedAnimatedScroll));
    m_pContainer->runAction(CCSequence::create(scroll, expire, NULL));
	//负责不停调用delegate
    this->schedule(schedule_selector(CCScrollView::performedAnimatedScroll));
}

4.小结

看到这里,CCScrollView里面属于自己独有部分的东西基本已经看完。

能够看出:

1.CCScrollView支持两种操作,滚动和缩放。

2.CCScrollView通过delegate将数据与界面解耦。

3.CCScrollView本质是一个CClayer,他展示的是自己内部的container。而且CCScrollView的触摸以及展示是依据ViewSize 还不是本身的SIze决定的。

时间: 04-28

cocos2d-x CCScrollView 源代码分析的相关文章

Java中arraylist和linkedlist源代码分析与性能比較

Java中arraylist和linkedlist源代码分析与性能比較 1,简单介绍 在java开发中比較经常使用的数据结构是arraylist和linkedlist,本文主要从源代码角度分析arraylist和linkedlist的性能. 2,arraylist源代码分析 Arraylist底层的数据结构是一个对象数组.有一个size的成员变量标记数组中元素的个数,例如以下图: * The array buffer into which the elements of the ArrayLis

转:RTMPDump源代码分析

0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://.也提供 Android 版本. 最近研究了一下它内部函数调用的关系. 下面列出几个主要的函数的调用关系. RTMPDump用于下载RTMP流媒体的函数Download: 用于建立网络连接(NetConnect)的函数Connect: 用于建立网络流(NetStream)的函数 rtmpdump源代码

Kafka SocketServer源代码分析

Kafka SocketServer源代码分析 标签: kafka 本文将详细分析Kafka SocketServer的相关源码. 总体设计 Kafka SocketServer是基于Java NIO来开发的,采用了Reactor的模式,其中包含了1个Acceptor负责接受客户端请求,N个Processor负责读写数据,M个Handler来处理业务逻辑.在Acceptor和Processor,Processor和Handler之间都有队列来缓冲请求. kafka.network.Accepto

pomelo源代码分析(一)

千里之行始于足下,一直说想了解pomelo,对pomelo有兴趣,但一直迟迟没有去碰,尽管对pomelo进行源代码分析,在网络上肯定不止我一个,已经有非常优秀的前辈走在前面,如http://golanger.cn/,在阅读Pomelo代码的时候,已经连载到了11篇了,在我的源代码分析參考了该博客,当然,也会添?我对pomelo的理解,借此希望能提高一下自己对node.js的了解和学习一些优秀的设计. 开发环境:win7 调试环境:webstorm5.0 node.js版本号:v0.8.21 源代

Jafka源代码分析——随笔

Kafka是一个分布式的消息中间件,可以粗略的将其划分为三部分:Producer.Broker和Consumer.其中,Producer负责产生消息并负责将消息发送给Kafka:Broker可以简单的理解为Kafka集群中的每一台机器,其负责完成消息队列的主要功能(接收消息.消息的持久化存储.为Consumer提供消息.消息清理.....):Consumer从Broker获取消息并进行后续的操作.每个broker会有一个ID标识,该标识由人工在配置文件中配置. Kafka中的消息隶属于topic

ftp server源代码分析20140602

当前是有些工具比如apktool,dextojar等是可以对我们android安装包进行反编译,获得源码的.为了减少被别人破解,导致源码泄露,程序被别人盗取代码,等等.我们需要对代码进行混淆,android的sdk中为我们提供了ProGrard这个工具,可以对代码进行混淆(一般是用无意义的名字来重命名),以及去除没有使用到的代码,对程序进行优化和压缩,这样可以增加你想的难度.最近我做的项目,是我去配置的混淆配置,因此研究了一下,这里分享一下. 如何启用ProGuard ant项目和eclipse

Spark SQL之External DataSource外部数据源(二)源代码分析

上周Spark1.2刚公布,周末在家没事,把这个特性给了解一下,顺便分析下源代码,看一看这个特性是怎样设计及实现的. /** Spark SQL源代码分析系列文章*/ (Ps: External DataSource使用篇地址:Spark SQL之External DataSource外部数据源(一)演示样例 http://blog.csdn.net/oopsoom/article/details/42061077) 一.Sources包核心 Spark SQL在Spark1.2中提供了Exte

【转载】linux环境下tcpdump源代码分析

linux环境下tcpdump源代码分析 原文时间 2013-10-11 13:13:02   原文链接   主题 Tcpdump 作者:韩大卫 @ 吉林师范大学 tcpdump.c 是tcpdump 工具的main.c, 本文旨对tcpdump的框架有简单了解,只展示linux平台使用的一部分核心代码. Tcpdump 的使用目的就是打印出指定条件的报文,即使有再多的正则表达式作为过滤条件.所以只要懂得tcpdump -nXXi eth0 的实现原理即可. 进入main之前,先看一些头文件 n

Android万能适配器base-adapter-helper的源代码分析

项目地址:https://github.com/JoanZapata/base-adapter-helper 1. 功能介绍 1.1. base-adapter-helper base-adapter-helper 是对传统的 BaseAdapter ViewHolder 模式的一个封装.主要功能就是简化我们书写 AbsListView 的 Adapter 的代码,如 ListView,GridView. 1.2 基本使用 mListView.setAdapter(mAdapter = new