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

 

以上就是對這三種用法的簡單解釋,如果需要查看其工作原理,還是要深入到源碼中,進行了解。

 

 
        

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM