Original address: http://blog.csdn.net/u011225840/article/details/30033501
1. Inheritance tree structure
You can see that CCScrollView is essentially a kind of CCLayer with all the properties and methods of the layer.Source analysis for CCLayer will follow.
2. Important Members
1. CCScrollViewDelegate* m_pDelegate;
In cocos2d-x, many delegates are used.Here's a brief explanation of delegate.(As for the difference between delegate and proxy, please refer to the three proxy cases in headfirst first first, then you can distinguish between google, which will not be repeated here.)
XXXDelegate encapsulates interfaces (the implementation in c++ is a virtual function and a pure virtual function that must be implemented). There are some methods in class A, such as getDataNum in View. View determines how an interface is displayed based on the amount of data, but A has no direct relationship with data.So, in View
getDataNum in A calls the DataDelegate method inside A to get how much data it has.As to what this data is, you only need to implement a specific class of DataDelegate.This way, the coupling between the View and the data is very low.View only relies on abstract DataDelegate.
In subsequent source analysis, you can see the beauty of delegate.
3. Source Code Parsing
3.1 ccTouchBegan
For the important part of ccTouchBegan, I added a comment.As you can see from the code, CCscrollView supports single and double touch.
-
bool CCScrollView::ccTouchBegan(CCTouch* touch, CCEvent* event)
-
{
-
if (!this->isVisible() || !m_bCanTouch)
-
{
-
return false;
-
}
-
-
CCRect frame = getViewRect();
-
-
-
-
-
-
-
-
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);
-
}
-
-
-
-
-
if (m_pTouches->count() == 1)
-
{
-
m_tTouchPoint = this->convertTouchToNodeSpace(touch);
-
m_bTouchMoved = false;
-
m_bDragging = true;
-
m_tScrollDistance = ccp(0.0f, 0.0f);
-
m_fTouchLength = 0.0f;
-
}
-
-
-
-
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
Same as above, with comments in the code
-
void CCScrollView::ccTouchMoved(CCTouch* touch, CCEvent* event)
-
{
-
-
if (!this->isVisible())
-
{
-
return;
-
}
-
-
-
-
-
if(this->m_bScrollLock)
-
{
-
return;
-
}
-
-
if (m_pTouches->containsObject(touch))
-
{
-
-
-
-
-
if (m_pTouches->count() == 1 && m_bDragging)
-
{
-
CCPoint moveDistance, newPoint, maxInset, minInset;
-
CCRect frame;
-
float newX, newY;
-
-
frame = getViewRect();
-
-
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 )
-
{
-
-
return;
-
}
-
-
if (!m_bTouchMoved)
-
{
-
moveDistance = CCPointZero;
-
}
-
-
m_tTouchPoint = newPoint;
-
m_bTouchMoved = true;
-
-
if (frame.containsPoint(this->convertToWorldSpace(newPoint)))
-
{
-
-
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;
-
-
m_tScrollDistance = moveDistance;
-
this->setContentOffset(ccp(newX, newY));
-
}
-
}
-
-
-
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);
-
}
-
}
-
}
On a single touch, a function called setContentOffset was called.Continue to analyze contentOffset below.
setContentOffset
-
void CCScrollView::setContentOffset(CCPoint offset, bool animated)
-
{
-
-
if (animated)
-
{
-
this->setContentOffsetInDuration(offset, BOUNCE_DURATION);
-
}
-
-
else
-
{
-
-
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);
-
}
-
}
-
}
An important function, setZoomScale, was called when touching with two points.
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;
-
}
-
-
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);
-
-
if (m_pDelegate != NULL)
-
{
-
m_pDelegate->scrollViewDidZoom(this);
-
}
-
-
this->setContentOffset(ccpAdd(m_pContainer->getPosition(),offset));
-
}
-
}
3.3 ccTouchEnded
If you can persist in seeing this, you can already see the dawn of victory.
-
void CCScrollView::ccTouchEnded(CCTouch* touch, CCEvent* event)
-
{
-
if (!this->isVisible())
-
{
-
return;
-
}
-
-
if (m_pTouches->containsObject(touch))
-
{
-
-
if (m_pTouches->count() == 1 && m_bTouchMoved)
-
{
-
this->schedule(schedule_selector(CCScrollView::deaccelerateScrolling));
-
}
-
m_pTouches->removeObject(touch);
-
-
-
}
-
-
if (m_pTouches->count() == 0)
-
{
-
m_bDragging = false;
-
m_bTouchMoved = false;
-
}
-
}
deaccelerateScrolling
In this function, there's a place where you don't understand it and ask the God to point you out
-
void CCScrollView::deaccelerateScrolling(float dt)
-
{
-
-
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);
-
-
m_pContainer->setPosition(ccpAdd(m_pContainer->getPosition(), m_tScrollDistance));
-
-
-
if (m_bBounceable)
-
{
-
maxInset = m_fMaxInset;
-
minInset = m_fMinInset;
-
}
-
else
-
{
-
maxInset = this->maxContainerOffset();
-
minInset = this->minContainerOffset();
-
}
-
-
-
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);
-
-
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);
-
}
-
-
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);
-
-
expire = CCCallFuncN::create(this, callfuncN_selector(CCScrollView::stoppedAnimatedScroll));
-
m_pContainer->runAction(CCSequence::create(scroll, expire, NULL));
-
-
this->schedule(schedule_selector(CCScrollView::performedAnimatedScroll));
-
}
4. Summary
Seeing this, the CCScrollView has basically finished what belongs to its own unique part.You can see that:
1.CCScrollView supports two operations, scrolling and zooming.
2.CCScrollView decouples data from the interface through delegate.
3.CCScrollView is essentially a CClayer, which displays its own internal container, and the touching and display of CCScrollView is based on the SIze that ViewSize is not.