首页 > 代码库 > cocos2d-x CCScrollView 源代码分析
cocos2d-x CCScrollView 源代码分析
版本号源代码来自2.x,转载请注明
另我实现了能够循环的版本号http://blog.csdn.net/u011225840/article/details/31354703
1.继承树结构
2.重要的成员
以下简单的说明下delegate这样的模式。
(至于delegate与proxy的差别,请先參考下headfirst中的proxy三种情况,然后能够google差别。这里不再赘述。)
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; } /* 假设此时不同意滚动,则退出。单点触摸时。调用了一个函数叫setContentOffset。以下继续分析contentOffset。这个能够通过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
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
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.小结
能够看出:
cocos2d-x CCScrollView 源代码分析