cocos2dx CCScrollView 逐行源码分析

才工作不久,经验很少,阅读源码学习cocos,大家仅供参考就好

目录

1、scrollview()默认构造

2、含参和缺省create()

 3、带参初始化滑动容器,initwithviewsize()

4、判断给定节点是否在当前容器可见,isnodevisable

5、暂停当前容器,pause()

6、恢复,resume()

7、获取触摸状态,istouchenabled

8、设置触摸状态,settouchenabled

9、设置触摸吞噬,setswallowtouches

10、设置content偏移,setcontentoffset

11、主动设置位置变化时候的滚动动画,setContentOffsetInDuration

12、停止动画函数,stopAnimatedContentOffset

13、获取当前偏移量,getContentOffset

14、设置缩放,setZoomScale

15、获取整体缩放比例,getzoomscale

16、有动画的进行缩放,setZoomScale

17、有动画的缩放,setZoomScaleInDuration

18、updateTweenAction

19、设置大小,setViewSize

20、返回内部容器,getContainer

21、设置内部容器,setContainer

22、查询是否有隐藏的父节点,hasVisibleParents()

23、以适当偏移修正展示,relocatecontainer

24、自动滚动,deaccelerateScrolling

25、停止滚动条,stoppedAnimatedScroll

26、设置自动委托调用方法,performedAnimatedScroll

27、获取和设置大小

28、更新容器的内部偏移,updateInset

29、不同方式增加和删除child

30、渲染,visit

31、触摸开始,ontouchbegan

32、触摸移动中,ontouchmoved

33、触摸结束,ontouchended

34、触摸取消,ontouchcancelled

35、获取当前容器矩阵大小,getviewrect



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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值