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;
} /*
假设此时不同意滚动,则退出。 这个能够通过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
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 源代码分析的更多相关文章
- cocos2d-x 源代码分析 总文件夹
这篇博客用来整理与cocos2d-x相关的工作,仅仅要有新的分析.扩展或者改动,都会更改此文章. 祝大家愉快~ 1.源代码分析 1.CCScrollView源代码分析 http://blog.csdn ...
- Cocos2d-x 源代码分析 : Scheduler(定时器) 源代码分析
源代码版本号 3.1r,转载请注明 我也最终不out了,開始看3.x的源代码了.此时此刻的心情仅仅能是wtf! !!!!!!! !.只是也最终告别CC时代了. cocos2d-x 源代码分析文件夹 h ...
- cocos2d-x 源代码分析 : Ref (CCObject) 源代码分析 cocos2d-x内存管理策略
从源代码版本号3.x.转载请注明 cocos2d-x 总的文件夹的源代码分析: http://blog.csdn.net/u011225840/article/details/31743129 1.R ...
- android-plugmgr源代码分析
android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...
- Twitter Storm源代码分析之ZooKeeper中的目录结构
徐明明博客:Twitter Storm源代码分析之ZooKeeper中的目录结构 我们知道Twitter Storm的所有的状态信息都是保存在Zookeeper里面,nimbus通过在zookeepe ...
- 转:SDL2源代码分析
1:初始化(SDL_Init()) SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中 ...
- 转:RTMPDump源代码分析
0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://. ...
- 转:ffdshow 源代码分析
ffdshow神奇的功能:视频播放时显示运动矢量和QP FFDShow可以称得上是全能的解码.编码器.最初FFDShow只是mpeg视频解码器,不过现在他能做到的远不止于此.它能够解码的视频格式已经远 ...
- UiAutomator源代码分析之UiAutomatorBridge框架
上一篇文章<UIAutomator源代码分析之启动和执行>我们描写叙述了uitautomator从命令行执行到载入測试用例执行測试的整个流程.过程中我们也描写叙述了UiAutomatorB ...
随机推荐
- 为什么java的构造方法中this()或者super()要放在第一行
java的构造方法中如果自己显性的调用super()的时候一定要放在第一行,如不是的话就会报错. 为什么一定要在第一行? super()在第一行的原因就是: 子类有可能访问了父类对象, 比如在构造函数 ...
- PAT甲级1018. Public Bike Management
PAT甲级1018. Public Bike Management 题意: 杭州市有公共自行车服务,为世界各地的游客提供了极大的便利.人们可以在任何一个车站租一辆自行车,并将其送回城市的任何其他车站. ...
- mybatis源码分析(8)-----事务(mybatis管理、spring管理)
写在前面 接口:MyBatis的事务Transaction的接口有一下实现类 JdbcTransaction 由jdbc管理的事务(即利用Connection对象完成对事务的提交(commit()). ...
- HAproxy + keepalived 实现双机热备
一.HAProxy简介: HAProxy提供高可用性.负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费.快速并且可靠的一种解决方案.HAProxy特别适用于那些负载特大的web站点, ...
- iOS开发中虚拟键盘相关的坑
初学者在学习iOS开发时,遇到在一个textField中输入完内容后却发现虚拟键盘无法隐藏起来而不知所措的情况的人一定不占少数吧.这篇文章就说说我遇到的和虚拟键盘有关的三个问题及解决对策. 在模拟器测 ...
- 反激 (Flyback) 控制器 -- LTC3803
LTC3803 - 采用 ThinSOT 封装的恒定频率.电流模式.反激式 DC/DC 控制器 特点 VIN 和 VOUT 仅受限于外部元件 可调斜率补偿 内部软起动 200kHz 恒定频率操作 ±1 ...
- python笔记20-yaml文件写入(ruamel.yaml)
前言 yaml作为配置文件是非常友好的一种格式,前面一篇讲了yaml的一些基础语法和读取方法,本篇继续讲yaml文件写入方法 用yaml模块写入字典嵌套字典这种复杂的数据,会出现大括号{ },不是真正 ...
- java中引用的原理
转自:http://blog.163.com/xubin_3@126/blog/static/112987702200962211145825/ 在Java中的引用类型,是指除了基本的变量类型之外的所 ...
- 使用Struts2服务端与android交互
转自:http://www.cnblogs.com/android-html5/archive/2011/09/25/2534107.html android--使用Struts2服务端与androi ...
- java代码逆向工程生成uml
今天在看一个模拟器的源码,一个包里有多个类,一个类里又有多个属性和方法,如果按顺序看下来,不仅不能对整个模拟器的框架形成一个大致的认识,而且只会越看越混乱,所以,想到有没有什么工具可以将这些个类以及它 ...