才工作不久,经验很少,阅读源码学习cocos,大家仅供参考就好
目录
3、带参初始化滑动容器,initwithviewsize()
4、判断给定节点是否在当前容器可见,isnodevisable
10、设置content偏移,setcontentoffset
11、主动设置位置变化时候的滚动动画,setContentOffsetInDuration
12、停止动画函数,stopAnimatedContentOffset
17、有动画的缩放,setZoomScaleInDuration
22、查询是否有隐藏的父节点,hasVisibleParents()
23、以适当偏移修正展示,relocatecontainer
25、停止滚动条,stoppedAnimatedScroll
26、设置自动委托调用方法,performedAnimatedScroll
1、scrollview()默认构造
没什么好说的,初始化列表初始化,默认滑动方向初始为 both ,水平垂直两个方向都可以
ScrollView::ScrollView()
: _delegate(nullptr)
, _direction(Direction::BOTH)
, _dragging(false)
, _container(nullptr)
, _touchMoved(false)
, _bounceable(false)
, _clippingToBounds(false)
, _touchLength(0.0f)
, _minScale(0.0f)
, _maxScale(0.0f)
, _scissorRestored(false)
, _touchListener(nullptr)
, _animatedScrollAction(nullptr)
{
}
2、含参和缺省create()
cocos提供了两个create函数,这个是有参构造,下一个是缺省构造。该函数create一个新对象,传参为滑动容器大小size 和container 其内部容器,后者默认可传空
来看函数内部实现,首先new一个新的对象出来,之后判空和进行初始化,成功后再将其放到生命周期管理池中去,如果创建失败安全删除。删除的宏定义如下
#define CC_SAFE_DELETE(p) do { delete (p); (p) = nullptr; } while(0)
ScrollView* ScrollView::create(Size size, Node* container/* = nullptr*/)
{
ScrollView* pRet = new (std::nothrow) ScrollView();
if (pRet && pRet->initWithViewSize(size, container))
{
pRet->autorelease();
}
else
{
CC_SAFE_DELETE(pRet);
}
return pRet;
}
缺省构造的create如下,只不过这个地方在初始化的时候调用的是Init(),在init()中去调用this->initWithViewSize(Size(200, 200), nullptr); 默认生成200*200展示范围的组件
ScrollView* ScrollView::create()
{
ScrollView* pRet = new (std::nothrow) ScrollView();
if (pRet && pRet->init())
{
pRet->autorelease();
}
else
{
CC_SAFE_DELETE(pRet);
}
return pRet;
}
3、带参初始化滑动容器,initwithviewsize()
首先layer:init()函数 设置一个默认layer为当前设置的分辨率大小,接下来就是一些初始化的 内容,setIgnoreAnchorPointForPosition()不使用锚点,默认以0,0为坐标点
bool Layer::init()
{
Director * director = Director::getInstance();
setContentSize(director->getWinSize());
return true;
}
bool ScrollView::initWithViewSize(Size size, Node *container/* = nullptr*/)
{
if (Layer::init())
{
_container = container;
if (!this->_container)
{
_container = Layer::create();
_container->setIgnoreAnchorPointForPosition(false);
_container->setAnchorPoint(Vec2(0.0f, 0.0f));
}
this->setViewSize(size);
setTouchEnabled(true);
_touches.reserve(EventTouch::MAX_TOUCHES);
_delegate = nullptr;
_bounceable = true;
_clippingToBounds = true;
//_container->setContentSize(Size::ZERO);
_direction = Direction::BOTH;
_container->setPosition(0.0f, 0.0f);
_touchLength = 0.0f;
this->addChild(_container);
_minScale = _maxScale = 1.0f;
return true;
}
return false;
}
4、判断给定节点是否在当前容器可见,isnodevisable
传参为一个node,获取四元组{x,y,width,hright},并依次按缩放比例进行缩放,调用intersetcsRect函数进行相交判断,并返回结果,疑问,这个地方的缩放如果x,y并不是一样的缩放比例怎么办,在getzoomscale()函数中会去调用到getscale(),如果xy方向缩放不一致会触发断言,
float Node::getScale() const
{
CCASSERT( _scaleX == _scaleY, "CCNode#scale. ScaleX != ScaleY. Don't know which one to return");
return _scaleX;
}
bool ScrollView::isNodeVisible(Node* node)
{
const Vec2 offset = this->getContentOffset();
const Size size = this->getViewSize();
const float scale = this->getZoomScale();
Rect viewRect;
viewRect = Rect(-offset.x/scale, -offset.y/scale, size.width/scale, size.height/scale);
return viewRect.intersectsRect(node->getBoundingBox());
}
5、暂停当前容器,pause()
首先暂停本身动作,然后遍历子容器,并依次调用pause()暂停
暂停调用的都有计时器、动作管理、监听触发器
void Node::pause()
{
_scheduler->pauseTarget(this);
_actionManager->pauseTarget(this);
_eventDispatcher->pauseEventListenersForTarget(this);
}
void ScrollView::pause(Ref* /*sender*/)
{
_container->pause();
auto& children = _container->getChildren();
for(const auto &child : children) {
child->pause();
}
}
6、恢复,resume()
首先遍历子容器,对其依次调用resume,最后调用本身resume恢复
恢复时对暂停的计时器、动作管理、监听触发器进行恢复
void Node::resume()
{
_scheduler->resumeTarget(this);
_actionManager->resumeTarget(this);
_eventDispatcher->resumeEventListenersForTarget(this);
}
void ScrollView::resume(Ref* /*sender*/)
{
auto& children = _container->getChildren();
for(const auto &child : children) {
child->resume();
}
_container->resume();
}
7、获取触摸状态,istouchenabled
bool ScrollView::isTouchEnabled() const
{
return _touchListener != nullptr;
}
8、设置触摸状态,settouchenabled
首先移除触摸监听,并将其置空,
如果enabled为true,创建一个新的单点触摸监听,同时设置触摸吞噬为true,不向下传递,setSwallowTouches(true),接下来绑定触摸开始,触摸移动,触摸结束,触摸取消四个事件的监听回调,最后添加监听,
如果enabled为false,则清除拖动状态dragging,触摸移动状态touchmoved,以及多点触摸事件数组touches
void ScrollView::setTouchEnabled(bool enabled)
{
_eventDispatcher->removeEventListener(_touchListener);
_touchListener = nullptr;
if (enabled)
{
_touchListener = EventListenerTouchOneByOne::create();
_touchListener->setSwallowTouches(true);
_touchListener->onTouchBegan = CC_CALLBACK_2(ScrollView::onTouchBegan, this);
_touchListener->onTouchMoved = CC_CALLBACK_2(ScrollView::onTouchMoved, this);
_touchListener->onTouchEnded = CC_CALLBACK_2(ScrollView::onTouchEnded, this);
_touchListener->onTouchCancelled = CC_CALLBACK_2(ScrollView::onTouchCancelled, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(_touchListener, this);
}
else
{
_dragging = false;
_touchMoved = false;
_touches.clear();
}
}
9、设置触摸吞噬,setswallowtouches
首先判断是否有触摸监听,如果无监听则触摸吞噬也无意义,不设置,如果有触摸监听则去设置触摸吞噬,那这样的话每次设置完触摸监听还需要去设置一下触摸吞噬,二者顺序不能颠倒
void ScrollView::setSwallowTouches(bool needSwallow)
{
if (_touchListener != nullptr)
{
_touchListener->setSwallowTouches(needSwallow);
}
}
10、设置content偏移,setcontentoffset
animated判断是否有动画,如果有动画滚动,则去调用setContentOffsetInDuration函数,见11、介绍
无动画,bounceable判断是否允许回弹,如果不允许则需要修正偏移量,保证其不会超出展示范围之外,然后设置位置坐标,delegate 滚动视图代理不空的话,进行视图的滚动scrollViewDidScroll 疑问:目前在该文件中未发现对_delegate非空的设置,只有初始化的时候置为nullptr,这个后续去看下其他地方有无修改,
.h文件中找了set函数可以设置委托
void ScrollView::setContentOffset(Vec2 offset, bool animated/* = false*/)
{
if (animated)
{ //animate scrolling
this->setContentOffsetInDuration(offset, BOUNCE_DURATION);
}
else
{ //set the container position directly
if (!_bounceable)
{
const Vec2 minOffset = this->minContainerOffset();
const Vec2 maxOffset = this->maxContainerOffset();
offset.x = MAX(minOffset.x, MIN(maxOffset.x, offset.x));
offset.y = MAX(minOffset.y, MIN(maxOffset.y, offset.y));
}
_container->setPosition(offset);
if (_delegate != nullptr)
{
_delegate->scrollViewDidScroll(this);
}
}
}
11、主动设置位置变化时候的滚动动画,setContentOffsetInDuration
_animatedScrollAction 滑动容器的内部动画,保存起来,如果不空,停止动画播放
接下来注册滚动条滚动动画和回调函数,执行滚动动画
void ScrollView::setContentOffsetInDuration(Vec2 offset, float dt)
{
FiniteTimeAction *scroll, *expire;
if (_animatedScrollAction) {
stopAnimatedContentOffset();
}
scroll = MoveTo::create(dt, offset);
expire = CallFuncN::create(CC_CALLBACK_1(ScrollView::stoppedAnimatedScroll,this));
_animatedScrollAction = _container->runAction(Sequence::create(scroll, expire, nullptr));
_animatedScrollAction->retain();
this->schedule(CC_SCHEDULE_SELECTOR(ScrollView::performedAnimatedScroll));
}
12、停止动画函数,stopAnimatedContentOffset
将保存的动画对象释放,停止动画
void ScrollView::stopAnimatedContentOffset() {
stopAction(_animatedScrollAction);
_animatedScrollAction->release();
_animatedScrollAction = nullptr;
stoppedAnimatedScroll(this);
}
13、获取当前偏移量,getContentOffset
Vec2 ScrollView::getContentOffset()
{
return _container->getPosition();
}
14、设置缩放,setZoomScale
首先判断是否当前缩放已满足要设定的缩放比例,如果不是则执行缩放处理
touchlength是触摸时双指之间的距离
疑问:
if (_touchLength == 0.0f)
{
center.set(_viewSize.width*0.5f, _viewSize.height*0.5f);
center = this->convertToWorldSpace(center);
}
这个地方是在干什么还不是很清楚,有时间在继续研究
如果不是一个触摸点,则将触摸点touchpoint赋值给center对象
oldCenter = _container->convertToNodeSpace(center);将缩放前的比例保存下来,
_container->setScale(MAX(_minScale, MIN(_maxScale, s)));设置缩放
newCenter = _container->convertToWorldSpace(oldCenter);转换缩放前的坐标,按新的缩放比例进行坐标缩放
convertToNodeSpace这个函数后续继续看下
接下来是更新展示区域,对其进行偏移量设置,然后设置到按当前偏移量计算后需要展示的位置
void ScrollView::setZoomScale(float s)
{
if (_container->getScale() != s)
{
Vec2 oldCenter, newCenter;
Vec2 center;
if (_touchLength == 0.0f)
{
center.set(_viewSize.width*0.5f, _viewSize.height*0.5f);
center = this->convertToWorldSpace(center);
}
else
{
center = _touchPoint;
}
oldCenter = _container->convertToNodeSpace(center);
_container->setScale(MAX(_minScale, MIN(_maxScale, s)));
newCenter = _container->convertToWorldSpace(oldCenter);
const Vec2 offset = center - newCenter;
if (_delegate != nullptr)
{
_delegate->scrollViewDidZoom(this);
}
this->setContentOffset(_container->getPosition() + offset);
}
}
15、获取整体缩放比例,getzoomscale
这个地方获取缩放比例的时候,当前的x,y轴方向的缩放比例应该是一致的,不然会触发断言
float ScrollView::getZoomScale()
{
return _container->getScale();
}
16、有动画的进行缩放,setZoomScale
如果有动画的话,animated设置为true,会去调用setZoomScaleInDuration函数,不过这个地方的BOUNCE_DURATION默认为0.15f
void ScrollView::setZoomScale(float s, bool animated)
{
if (animated)
{
this->setZoomScaleInDuration(s, BOUNCE_DURATION);
}
else
{
this->setZoomScale(s);
}
}
17、有动画的缩放,setZoomScaleInDuration
dt为动画执行的时间,这个主要是判断下是否时间大于0,大于0执行动画动作,否则执行普通的缩放函数即可
void ScrollView::setZoomScaleInDuration(float s, float dt)
{
if (dt > 0)
{
if (_container->getScale() != s)
{
ActionTween *scaleAction;
scaleAction = ActionTween::create(dt, "zoomScale", _container->getScale(), s);
this->runAction(scaleAction);
}
}
else
{
this->setZoomScale(s);
}
}
18、updateTweenAction
这个函数目前没看太懂有什么实际意义,可能是后续要拓展?
void ScrollView::updateTweenAction(float value, const std::string& /*key*/)
{
this->setZoomScale(value);
}
19、设置大小,setViewSize
setcontentsize函数会按大小size比例乘以锚点坐标比例去计算实际的坐标
void ScrollView::setViewSize(Size size)
{
_viewSize = size;
Layer::setContentSize(size);
}
20、返回内部容器,getContainer
Node * ScrollView::getContainer()
{
return this->_container;
}
21、设置内部容器,setContainer
对要设置的目标容器先判空,如果空直接返回,避免将当前容器删除掉。
先删除容器的所有子组件,然后将当前内部容器设置为目标容器,对其设置坐标及锚点,将其addchild进当前节点,然后设置其大小
void ScrollView::setContainer(Node * pContainer)
{
// Make sure that '_container' has a non-nullptr value since there are
// lots of logic that use '_container'.
if (nullptr == pContainer)
return;
this->removeAllChildrenWithCleanup(true);
this->_container = pContainer;
this->_container->setIgnoreAnchorPointForPosition(false);
this->_container->setAnchorPoint(Vec2(0.0f, 0.0f));
this->addChild(this->_container);
this->setViewSize(this->_viewSize);
}
22、查询是否有隐藏的父节点,hasVisibleParents()
递归查询父节点是否存在设置为显示隐藏的节点
bool ScrollView::hasVisibleParents() const
{
auto parent = this->getParent();
for( auto c = parent; c != nullptr; c = c->getParent() )
{
if( !c->isVisible() )
{
return false;
}
}
return true;
}
23、以适当偏移修正展示,relocatecontainer
设置旧坐标,旧坐标按当前实际坐标记录
newx,newy赋初始值分别为当前坐标的x,y
mincontaineroffset函数如下:
首先获取当前锚点,分别获取缩放后的width和缩放后的height,返回经过锚点修正的坐标
Vec2 ScrollView::minContainerOffset()
{
Point anchorPoint = _container->isIgnoreAnchorPointForPosition()?Point::ZERO:_container->getAnchorPoint();
float contW = _container->getContentSize().width * _container->getScaleX();
float contH = _container->getContentSize().height * _container->getScaleY();
return Vec2(_viewSize.width - (1 - anchorPoint.x) * contW, _viewSize.height - (1 - anchorPoint.y) * contH);
}
接下来是对坐标的进一步修正,至于这部分为什么这么修正,以及水平和垂直的修正顺序有什么先后要求,这个后续在研究,这里标记一下
void ScrollView::relocateContainer(bool animated)
{
Vec2 oldPoint, min, max;
float newX, newY;
min = this->minContainerOffset();
max = this->maxContainerOffset();
oldPoint = _container->getPosition();
newX = oldPoint.x;
newY = oldPoint.y;
if (_direction == Direction::BOTH || _direction == Direction::HORIZONTAL)
{
newX = MAX(newX, min.x);
newX = MIN(newX, max.x);
}
if (_direction == Direction::BOTH || _direction == Direction::VERTICAL)
{
newY = MIN(newY, max.y);
newY = MAX(newY, min.y);
}
if (newY != oldPoint.y || newX != oldPoint.x)
{
this->setContentOffset(Vec2(newX, newY), animated);
}
}
24、自动滚动,deaccelerateScrolling
判读是否可以滚动,不可以直接退出,并删除对应计时器
接下来设置回弹,设置回弹偏移量,设置滑动界面,并进行修正,具体修正逻辑及边界判断后续再继续研究
void ScrollView::deaccelerateScrolling(float /*dt*/)
{
if (_dragging)
{
this->unschedule(CC_SCHEDULE_SELECTOR(ScrollView::deaccelerateScrolling));
return;
}
float newX, newY;
Vec2 maxInset, minInset;
_container->setPosition(_container->getPosition() + _scrollDistance);
if (_bounceable)
{
maxInset = _maxInset;
minInset = _minInset;
}
else
{
maxInset = this->maxContainerOffset();
minInset = this->minContainerOffset();
}
newX = _container->getPosition().x;
newY = _container->getPosition().y;
_scrollDistance = _scrollDistance * SCROLL_DEACCEL_RATE;
this->setContentOffset(Vec2(newX,newY));
if ((fabsf(_scrollDistance.x) <= SCROLL_DEACCEL_DIST &&
fabsf(_scrollDistance.y) <= SCROLL_DEACCEL_DIST) ||
((_direction == Direction::BOTH || _direction == Direction::VERTICAL) && (newY >= maxInset.y || newY <= minInset.y)) ||
((_direction == Direction::BOTH || _direction == Direction::HORIZONTAL) && (newX >= maxInset.x || newX <= minInset.x)))
{
this->unschedule(CC_SCHEDULE_SELECTOR(ScrollView::deaccelerateScrolling));
this->relocateContainer(true);
}
}
25、停止滚动条,stoppedAnimatedScroll
void ScrollView::stoppedAnimatedScroll(Node * /*node*/)
{
this->unschedule(CC_SCHEDULE_SELECTOR(ScrollView::performedAnimatedScroll));
// After the animation stopped, "scrollViewDidScroll" should be invoked, this could fix the bug of lack of tableview cells.
if (_delegate != nullptr)
{
_delegate->scrollViewDidScroll(this);
}
}
26、设置自动委托调用方法,performedAnimatedScroll
void ScrollView::performedAnimatedScroll(float /*dt*/)
{
if (_dragging)
{
this->unschedule(CC_SCHEDULE_SELECTOR(ScrollView::performedAnimatedScroll));
return;
}
if (_delegate != nullptr)
{
_delegate->scrollViewDidScroll(this);
}
}
27、获取和设置大小
const Size& ScrollView::getContentSize() const
{
return _container->getContentSize();
}
void ScrollView::setContentSize(const Size & size)
{
if (this->getContainer() != nullptr)
{
this->getContainer()->setContentSize(size);
this->updateInset();
}
}
28、更新容器的内部偏移,updateInset
这个函数后续再仔细看下具体功能和逻辑
void ScrollView::updateInset()
{
if (this->getContainer() != nullptr)
{
_maxInset = this->maxContainerOffset();
_maxInset.set(_maxInset.x + _viewSize.width * INSET_RATIO,
_maxInset.y + _viewSize.height * INSET_RATIO);
_minInset = this->minContainerOffset();
_minInset.set(_minInset.x - _viewSize.width * INSET_RATIO,
_minInset.y - _viewSize.height * INSET_RATIO);
}
}
29、不同方式增加和删除child
void ScrollView::addChild(Node * child, int zOrder, int tag)
{
if (_container != child) {
_container->addChild(child, zOrder, tag);
} else {
Layer::addChild(child, zOrder, tag);
}
}
void ScrollView::removeChild(Node* node, bool cleanup)
{
if(_container != node)
{
_container->removeChild(node, cleanup);
}
else
{
Layer::removeChild(node, cleanup);
}
}
void ScrollView::removeAllChildrenWithCleanup(bool cleanup)
{
_container->removeAllChildrenWithCleanup(cleanup);
Layer::removeAllChildrenWithCleanup(cleanup);
}
void ScrollView::removeAllChildren()
{
removeAllChildrenWithCleanup(true);
}
void ScrollView::addChild(Node * child, int zOrder, const std::string &name)
{
if (_container != child)
{
_container->addChild(child, zOrder, name);
}
else
{
Layer::addChild(child, zOrder, name);
}
}
30、渲染,visit
先判断是不是可见,不可见则直接退出渲染,执行渲染,先渲染子容器zorder<0的,然后在渲染自己,最后渲染zorder>=0的
void ScrollView::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
// quick return if not visible
if (!isVisible())
{
return;
}
uint32_t flags = processParentFlags(parentTransform, parentFlags);
// IMPORTANT:
// To ease the migration to v3.0, we still support the Mat4 stack,
// but it is deprecated and your code should not rely on it
Director* director = Director::getInstance();
CCASSERT(nullptr != director, "Director is null when setting matrix stack");
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
this->beforeDraw();
bool visibleByCamera = isVisitableByVisitingCamera();
if (!_children.empty())
{
int i=0;
// draw children zOrder < 0
for( ; i < _children.size(); i++ )
{
Node *child = _children.at(i);
if ( child->getLocalZOrder() < 0 )
{
child->visit(renderer, _modelViewTransform, flags);
}
else
{
break;
}
}
// this draw
if (visibleByCamera)
this->draw(renderer, _modelViewTransform, flags);
// draw children zOrder >= 0
for( ; i < _children.size(); i++ )
{
Node *child = _children.at(i);
child->visit(renderer, _modelViewTransform, flags);
}
}
else if (visibleByCamera)
{
this->draw(renderer, _modelViewTransform, flags);
}
this->afterDraw();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
31、触摸开始,ontouchbegan
触摸开始的回调,先判断当前是否可见,并且其父节点有无可见设置。
这个地方的_touches.size()有点不太理解为什么要判断个>2,难道是默认两个手指的触摸属于一次合法触摸吗?
接下来是记录触摸点坐标
bool ScrollView::onTouchBegan(Touch* touch, Event* /*event*/)
{
if (!this->isVisible() || !this->hasVisibleParents())
{
return false;
}
Rect frame = getViewRect();
//dispatcher does not know about clipping. reject touches outside visible bounds.
if (_touches.size() > 2 ||
_touchMoved ||
!frame.containsPoint(touch->getLocation()))
{
return false;
}
if (std::find(_touches.begin(), _touches.end(), touch) == _touches.end())
{
_touches.push_back(touch);
}
if (_touches.size() == 1)
{ // scrolling
_touchPoint = this->convertTouchToNodeSpace(touch);
_touchMoved = false;
_dragging = true; //dragging started
_scrollDistance.setZero();
_touchLength = 0.0f;
}
else if (_touches.size() == 2)
{
_touchPoint = (this->convertTouchToNodeSpace(_touches[0]).getMidpoint(
this->convertTouchToNodeSpace(_touches[1])));
_touchLength = _container->convertTouchToNodeSpace(_touches[0]).getDistance(
_container->convertTouchToNodeSpace(_touches[1]));
_dragging = false;
}
return true;
}
32、触摸移动中,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
Vec2 moveDistance, newPoint;
Rect frame;
float newX, newY;
frame = getViewRect();
newPoint = this->convertTouchToNodeSpace(_touches[0]);
moveDistance = newPoint - _touchPoint;
float dis = 0.0f;
if (_direction == Direction::VERTICAL)
{
dis = moveDistance.y;
float pos = _container->getPosition().y;
if (!(minContainerOffset().y <= pos && pos <= maxContainerOffset().y)) {
moveDistance.y *= BOUNCE_BACK_FACTOR;
}
}
else if (_direction == Direction::HORIZONTAL)
{
dis = moveDistance.x;
float pos = _container->getPosition().x;
if (!(minContainerOffset().x <= pos && pos <= maxContainerOffset().x)) {
moveDistance.x *= BOUNCE_BACK_FACTOR;
}
}
else
{
dis = sqrtf(moveDistance.x*moveDistance.x + moveDistance.y*moveDistance.y);
float pos = _container->getPosition().y;
if (!(minContainerOffset().y <= pos && pos <= maxContainerOffset().y)) {
moveDistance.y *= BOUNCE_BACK_FACTOR;
}
pos = _container->getPosition().x;
if (!(minContainerOffset().x <= pos && pos <= maxContainerOffset().x)) {
moveDistance.x *= BOUNCE_BACK_FACTOR;
}
}
if (!_touchMoved && fabs(convertDistanceFromPointToInch(dis)) < MOVE_INCH )
{
//CCLOG("Invalid movement, distance = [%f, %f], disInch = %f", moveDistance.x, moveDistance.y);
return;
}
if (!_touchMoved)
{
moveDistance.setZero();
}
_touchPoint = newPoint;
_touchMoved = true;
if (_dragging)
{
switch (_direction)
{
case Direction::VERTICAL:
moveDistance.set(0.0f, moveDistance.y);
break;
case Direction::HORIZONTAL:
moveDistance.set(moveDistance.x, 0.0f);
break;
default:
break;
}
newX = _container->getPosition().x + moveDistance.x;
newY = _container->getPosition().y + moveDistance.y;
_scrollDistance = moveDistance;
this->setContentOffset(Vec2(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);
}
}
}
33、触摸结束,ontouchended
void ScrollView::onTouchEnded(Touch* touch, Event* /*event*/)
{
if (!this->isVisible())
{
return;
}
auto touchIter = std::find(_touches.begin(), _touches.end(), touch);
if (touchIter != _touches.end())
{
if (_touches.size() == 1 && _touchMoved)
{
this->schedule(CC_SCHEDULE_SELECTOR(ScrollView::deaccelerateScrolling));
}
_touches.erase(touchIter);
}
if (_touches.size() == 0)
{
_dragging = false;
_touchMoved = false;
}
}
34、触摸取消,ontouchcancelled
void ScrollView::onTouchCancelled(Touch* touch, Event* /*event*/)
{
if (!this->isVisible())
{
return;
}
auto touchIter = std::find(_touches.begin(), _touches.end(), touch);
if ( touchIter == _touches.end() )
return;
_touches.erase(touchIter);
if (_touches.size() == 0)
{
_dragging = false;
_touchMoved = false;
}
}
35、获取当前容器矩阵大小,getviewrect
这个地方的x,y是指的是屏幕坐标
返回一个四元组{x,y,width,height}
Rect ScrollView::getViewRect()
{
Vec2 screenPos = this->convertToWorldSpace(Vec2::ZERO);
float scaleX = this->getScaleX();
float scaleY = this->getScaleY();
for (Node *p = _parent; p != nullptr; p = p->getParent()) {
scaleX *= p->getScaleX();
scaleY *= p->getScaleY();
}
// Support negative scaling. Not doing so causes intersectsRect calls
// (eg: to check if the touch was within the bounds) to return false.
// Note, Node::getScale will assert if X and Y scales are different.
if(scaleX<0.f) {
screenPos.x += _viewSize.width*scaleX;
scaleX = -scaleX;
}
if(scaleY<0.f) {
screenPos.y += _viewSize.height*scaleY;
scaleY = -scaleY;
}
return Rect(screenPos.x, screenPos.y, _viewSize.width*scaleX, _viewSize.height*scaleY);
}