cocos2d-x中false,setSwallowTouches,stopPropagation的区别
研究到cocos2d-x触摸这一块了,3.0和2.0相比已经有了很大的不同,使用更加方便和容易理解了。
直接进入正题,解释下,标题中3个用法的区别
通常来说,应用程序中更多使用的是单点触摸,为了简化单点触摸的处理,cocos2dx将一个触摸事件分为单点触摸和多点触摸两种类型,相应的对应单点和多点两种订阅者类型
EventListenerTouchAllAtOnce分为四种状态,开始触摸,移动,结束,取消,
每一个状态的回调函数都包含当前所有处于该种状态的触摸点,开发者需要使用触摸点的ID来区分每一个触摸点
EventListenerTouchOneByOne则相反,他将触摸某个状态的多个触摸点分为多次事件通知 ,他也分为四种状态,开始触摸,移动,结束,取消。
不同的地方是他的开始触摸方法 onTouchBegan返回值为bool,并且onTouchBegan是必须实现的,否则将收不到任何触摸事件通知,而EventListenerTouchAllAtOnce的onTouchBegan没有返回值,他的实现与否不影响后续状态的回调。
下面就牵扯到了返回true与false的问题。
onTouchBegan的返回值用来告诉EventDispatcher是否应该讲触摸点后续的触摸状态传递给订阅者。如果为false,onTouchMoved
onTouchEnded和onTouchCancelled,将接受不到任何回调。
但是注意的是,这个返回值只对当前订阅者有效,对后续订阅者的回调是控制不了的。比如
Node A 订阅了 EventListenerTouchOneByOne和 EventListenerTouchAllAtOnce两种触摸类型,前者的onTouchBegan返回false,那么当点击了NodeA的时候(就是一点一抬,不涉及移动),触发了点击事件,那么执行的顺序为这个
1 EventListenerTouchOneByOne:onTouchBegan
2EventListenerTouchAllAtOnce:onTouchBegan
3EventListenerTouchAllAtOnce:onTouchEnd,
说明false只控制了EventListenerTouchOneByOne类型的订阅,对于Node订阅的EventListenerTouchAllAtOnce类型,控制不了,该怎么走还是怎么走。
再比如:
NodeA订阅了EventListenerTouchOneByOne,NodeB也订阅了EventListenerTouchOneByOne,并且NodeA的localZ小于NodeB的localZ,NodeA的onTouchBegan返回false,NodeB的onTouchBegan返回true,当触发点击事件之后,执行顺序为
1 NodeA EventListenerTouchOneByOne:onTouchBegan
2 NodeB EventListenerTouchOneByOne:onTouchBegan
3 NodeB EventListenerTouchOneByOne:onTouchEnd
说明NodeA的false只对订阅者nodeA起作用,对NodeB不起作用
这就是false的用法,如果想让当前订阅者继续往后面的状态传递,就返回true,不想让其传递,就返回false。
这种情况一般用在比如你点击了屏幕,但是坐标并没有落在某个sprite的可显示区域的上面,那么就返回false,反之返回true。比如下面得代码
bool isPointInNode(Vec2 pt, Node* node) { Vec2 locationInNode = node->convertToNodeSpace(pt); cocos2d::Size s = node->getContentSize(); cocos2d::Rect rect = cocos2d::Rect(0, 0, s.width, s.height); if (rect.containsPoint(locationInNode)) { return true; } return false; } touchOneByOneListener->onTouchBegan = [&](Touch* touch, Event* event){ auto target = static_cast<Sprite*>(event->getCurrentTarget()); if (this->isPointInNode(touch->getLocation(), target)) { target->setOpacity(180); return true; } return false; };
下面再说说setSwallowTouches的作用
当我们希望阻止一个触摸点向后面的订阅者继续分发,那么可以使用setSwallowTouches(true)来实现。
注意,swallowTouches设置需要在onTouchBegan返回true的时候才有效.
例如Sprite 和Sprite2都加到了同一个Node上,SpriteA后加,也就是SpriteA会先收到触摸事件,他们都订阅了EventListenerTouchOneByOne和
EventListenerTouchAllAtOnce
,如下代码
static const int TAG_BLUE_SPRITE = 101; static const int TAG_BLUE_SPRITE2 = 102; auto touchOneByOneListener = EventListenerTouchOneByOne::create(); touchOneByOneListener->setSwallowTouches(true); touchOneByOneListener->onTouchBegan = [&](Touch* touch, Event* event){ return true; }; touchOneByOneListener->onTouchEnded = [](Touch* touch, Event* event){ auto target = static_cast<Sprite*>(event->getCurrentTarget()); target->setOpacity(255); }; auto touchAllAtOnceListener = EventListenerTouchAllAtOnce::create(); touchAllAtOnceListener->onTouchesBegan = [=](const std::vector<Touch*>& touches, Event* event){ }; touchAllAtOnceListener->onTouchesEnded = [=](const std::vector<Touch*>& touches, Event* event){ }; Sprite* sprite; Sprite* sprite2; sprite = Sprite::create("CyanSquare.png"); sprite->setTag(TAG_BLUE_SPRITE); addChild(sprite, 100); sprite2 = Sprite::create("YellowSquare.png"); sprite2->setTag(TAG_BLUE_SPRITE2); addChild(sprite2, 100); _eventDispatcher->addEventListenerWithSceneGraphPriority(touchOneByOneListener->clone(), sprite); _eventDispatcher->addEventListenerWithSceneGraphPriority(touchAllAtOnceListener->clone(), sprite); _eventDispatcher->addEventListenerWithSceneGraphPriority(touchOneByOneListener->clone(), sprite2); _eventDispatcher->addEventListenerWithSceneGraphPriority(touchAllAtOnceListener->clone(), sprite2); sprite->setPosition(100,200); sprite2->setPosition(50,200);
注意我们设置 touchOneByOneListener->setSwallowTouches(true);
默认是false,通过设置了true,那么就可以阻止一个触摸点继续向后面的订阅者继续分发,
当点击事件触发后,执行顺序如下
1 spriteA touchOneByOneListener->onTouchBegan
2 spriteA touchOneByOneListener->onTouchEnd
因为spriteA的touchOneByOneListener->setSwallowTouches(true);他会阻止此后所有事件的分发,无论是spriteA自己的订阅类型EventListenerTouchAllAtOnce以及SpriteB的所有订阅类型。
这里说的一个应用的地方比如,spriteA订阅了单点触摸和多点触摸,但是他只想进行单点触摸的回调,而不想进行多点触摸的回调,那么就可以设置ontouchbegin返回true,并设置setSwallowTouches(true),这样就可以阻止多点触摸事件的分发。
最后说的是stopPropagation,他和setSwallowTouches类似,也是阻止触摸点后面订阅者的事件分发,但是不同的是setSwallowTouches阻止的很彻底,他会阻止后面所有状态的分发,began,end,cancelled,moved,并且必须ontouchbegan返回true才会起作用,而stopPropagation只是阻止某个状态的分发,比如move状态等。
stopPropagation只是停止当前当次触摸状态下的所有分发,例如Moved状态会触发多次,则第二次不受前一次的影响。某个状态也不会影响另一个状态的分发,例如move不影响end状态的分发,所有这些只需要明白,每个状态每次分发都是一次独立的事件通知
举例子如下:
static const int TAG_BLUE_SPRITE = 101; static const int TAG_BLUE_SPRITE2 = 102; auto touchOneByOneListener = EventListenerTouchOneByOne::create(); touchOneByOneListener->setSwallowTouches(false); touchOneByOneListener->onTouchBegan = [&](Touch* touch, Event* event){ auto target = static_cast<Sprite*>(event->getCurrentTarget()); if(target->getTag() == TAG_BLUE_SPRITE2) { printf("sprite2单点触摸--ontouchbegan\n"); } else{ printf("sprite单点触摸--ontouchbegan\n"); } // event->stopPropagation(); return true; }; touchOneByOneListener->onTouchEnded = [](Touch* touch, Event* event){ auto target = static_cast<Sprite*>(event->getCurrentTarget()); if(target->getTag() == TAG_BLUE_SPRITE2) { printf("sprite2单点触摸--ontouchend\n"); } else{ printf("sprite单点触摸--ontouchend\n"); } }; auto touchAllAtOnceListener = EventListenerTouchAllAtOnce::create(); touchAllAtOnceListener->onTouchesBegan = [=](const std::vector<Touch*>& touches, Event* event){ auto target = static_cast<Sprite*>(event->getCurrentTarget()); if(target->getTag() == TAG_BLUE_SPRITE2) { printf("sprite2多点触摸--ontouchbegan\n"); } else{ printf("sprite多点触摸--ontouchbegan\n"); } }; touchAllAtOnceListener->onTouchesEnded = [=](const std::vector<Touch*>& touches, Event* event){ auto target = static_cast<Sprite*>(event->getCurrentTarget()); if(target->getTag() == TAG_BLUE_SPRITE2) { printf("sprite2多点触摸--ontouchend\n"); } else{ printf("sprite多点触摸--ontouchend\n"); } }; Sprite* sprite; Sprite* sprite2; sprite = Sprite::create("CyanSquare.png"); sprite->setTag(TAG_BLUE_SPRITE); addChild(sprite, 100); sprite2 = Sprite::create("YellowSquare.png"); sprite2->setTag(TAG_BLUE_SPRITE2); addChild(sprite2, 100); _eventDispatcher->addEventListenerWithSceneGraphPriority(touchOneByOneListener->clone(), sprite); _eventDispatcher->addEventListenerWithSceneGraphPriority(touchAllAtOnceListener->clone(), sprite); _eventDispatcher->addEventListenerWithSceneGraphPriority(touchOneByOneListener->clone(), sprite2); _eventDispatcher->addEventListenerWithSceneGraphPriority(touchAllAtOnceListener->clone(), sprite2); sprite->setPosition(100,200); sprite2->setPosition(50,200);
执行代码结果如下
sprite2单点触摸--ontouchbegan
sprite单点触摸--ontouchbegan
sprite2多点触摸--ontouchbegan
sprite多点触摸--ontouchbegan
sprite2单点触摸--ontouchend
sprite单点触摸--ontouchend
sprite2多点触摸--ontouchend
sprite多点触摸--ontouchend
这个结果很容易理解,就是没有阻止事件的分发,一切都是按计划进行
如果 event->stopPropagation()的注释去掉,那么执行结果如下:
sprite2单点触摸--ontouchbegan
sprite2单点触摸--ontouchend
sprite2多点触摸--ontouchend
sprite多点触摸--ontouchend
这个的解释为:
sprite2的touchbegan先回调,然后stopprpagation,之后关于touchbegan的状态的事件分发就会停止,也就是sprite的touchbegan不会执行了,
而sprite的单点触摸的touchbegan不执行,那么sprite的单点触摸的其余状态也不会执行,但是sprite的多点触摸的touchbegan不执行,不影响多点触摸的其他状态的执行,所以sprite就执行了一个多点触摸的ontouchend
以上就是对这三种用法的简单解释,如果需要查看其工作原理,还是要深入到源码中,进行了解。