CCMenu超过CCScrollView边框还能点击处理,以及拖动CCMenuItem不响应点击事件

CCMenu超过CCScrollView边框还能点击处理,以及拖动CCMenuItem不响应点击事件

   最近做项目,遇到了这个问题,相信大家也会有很多遇见的,那就是CCScrollView或者CCTableView里面放入的按钮(这里的按钮我们用CCMenuItem来设置)超出滑动区域了还会响应点击事件,并且当点击在CCMenuItem上面的时候,还不能拖动,它会直接响应点击事件,让用户体验很不好,本来我对CCMenu底层的实现是一点也没看过的,对什么响应的优先级也是不怎么明白,正好趁这个机会,我看了看cocos2d-x底层的代码,就当一个学习机会了。

首先CCMenu继承的是CCLayer,CCLayer有一个虚函数,就是用来设置响应优先级的,我们看CCLayer的源代码:


/// Touch and Accelerometer related

void CCLayer::registerWithTouchDispatcher()
{
    CCTouchDispatcher* pDispatcher = CCDirector::sharedDirector()->getTouchDispatcher();

    // Using LuaBindings
    if (m_pScriptHandlerEntry)
    {
	    if (m_pScriptHandlerEntry->isMultiTouches())
	    {
	       pDispatcher->addStandardDelegate(this, 0);
	       LUALOG("[LUA] Add multi-touches event handler: %d", m_pScriptHandlerEntry->getHandler());
	    }
	    else
	    {
	       pDispatcher->addTargetedDelegate(this,
						m_pScriptHandlerEntry->getPriority(),
						m_pScriptHandlerEntry->getSwallowsTouches());
	       LUALOG("[LUA] Add touch event handler: %d", m_pScriptHandlerEntry->getHandler());
	    }
    }
    else
    {
        if( m_bTouchMode == kCCTouchesAllAtOnce ) {
            pDispatcher->addStandardDelegate(this, 0);
        } else {
            pDispatcher->addTargetedDelegate(this, m_bTouchPriority, true);
        }
    }
}
它先是获得了CCTouchDispatcher的指针,然后根据点击的不同类型,用不同的方法设置响应优先级,搜索一下m_bTouchPriority可以发现,初始化的时候也是把它设为了0,也就是说默认的CCLayer响应点击事件的优先级是0,然后我们再看看CCMenu的registerWithTouchDispatcher方法,代码如下

void CCMenu::registerWithTouchDispatcher()
{
    CCDirector* pDirector = CCDirector::sharedDirector();
    pDirector->getTouchDispatcher()->addTargetedDelegate(this, kCCMenuHandlerPriority, true);
}

写的很简单,就是把优先级设为了kCCMenuHandlerPriority,我们跟进去看

enum {
    //* priority used by the menu for the event handler
    kCCMenuHandlerPriority = -128,
};

这是一个枚举,把kCCMenuHandlerPriority设成了-128也就是说它的优先级是-128,我们知道优先级越低,会优先响应点击事件,也就是说CCMenu比CCLayer优先响应点击事件,我们知道CCLayer是没有边界的,也就是说CCMenu其实也没有边界,那么当它放入CCScrollview里面的时候虽然滑动除了CCSCrollView的边框,但是当点击到它的CCMenuItem区域的时候,它会比CCLayer优先响应点击事件,也就是说在整个屏幕上,CCMenuItem都是可以点击的,这就是问题所在了。


既然知道了是因为CCMenu的优先级高,那么很简单,我们继承CCMenu,重写registerWithTouchDispatcher方法,把CCMenu的优先级设低点就可以了,于是我们新写一个类继承CCMenu,起名叫WMMenu,废话不说了,贴代码最实在了

//
//  WMMenu.h
//  Test
//
//  Created by 江南岸 on 13-3-27.
//
//

#ifndef __Test__WMMenu__
#define __Test__WMMenu__

#include <iostream>
#include "cocos2d.h"
USING_NS_CC;

class WMMenu : public CCMenu
{
private:
    //设置一个点击区域,用于判断点击是否在这个区域内
    CCRect _rect;
    
    //是否滑动了MenuItem
    bool _isScroll;
    
public:
    //重写registerWithTouchDispatcher函数,设置优先级
    virtual void registerWithTouchDispatcher();

    //create方法,传进来的是可以响应点击的区域
    static WMMenu* create(CCRect rect);
    
    //重写三个touch函数
    virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
    
    virtual void ccTouchEnded(CCTouch *touch, CCEvent* event);
    
    virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);



    
};

#endif /* defined(__Test__WMMenu__) */

//
//  WMMenu.cpp
//  Test
//
//  Created by 江南岸 on 13-3-27.
//
//

#include "WMMenu.h"
void WMMenu::registerWithTouchDispatcher()
{
    CCTouchDispatcher* pDispatcher = CCDirector::sharedDirector()->getTouchDispatcher();
    
    //重新设置优先级,原来的优先级是-128,CCLayer的优先级是0,我们设置它的优先级为128
    pDispatcher->addTargetedDelegate(this, 128, true);
}

WMMenu* WMMenu::create(CCRect rect)
{
    WMMenu *m = new WMMenu();
    m->_rect = rect;
    
    if(m && m->init())
    {
        m->autorelease();
        return m;
    }
    
    CC_SAFE_DELETE(m);
    return NULL;
}

bool WMMenu::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
    //设置滑动为false,初始化
    _isScroll = false;
    
    //获取点击的点,判断是否在我们想要它所在的区域
    CCPoint pt = CCDirector::sharedDirector()->convertToGL(touch->getLocationInView());
    if(!_rect.containsPoint(pt))
    {
        return false;
    }
    
    if (m_eState != kCCMenuStateWaiting || ! m_bIsVisible || !m_bEnabled)
    {
        return false;
    }
    
    for (CCNode *c = this->m_pParent; c != NULL; c = c->getParent())
    {
        if (c->isVisible() == false)
        {
            return false;
        }
    }
    
    m_pSelectedItem = this->itemForTouch(touch);
    if (m_pSelectedItem)
    {
        m_eState = kCCMenuStateTrackingTouch;
        m_pSelectedItem->selected();
        return true;
    }
    
    return false;
}

void WMMenu::ccTouchMoved(CCTouch* touch, CCEvent* event)
{
    //如果滚动了,那么就设为true,并不做任何操作
    _isScroll = true;
    return;
}

void WMMenu::ccTouchEnded(CCTouch *touch, CCEvent* event)
{
    m_pSelectedItem->unselected();

    //判断如果滑动了,就不再触发目的事件了
    if (m_pSelectedItem && !_isScroll)
    {
        m_pSelectedItem->activate();
    }
    m_eState = kCCMenuStateWaiting;
}

我们看调用的代码

    CCScrollView *scroll = CCScrollView::create(CCSizeMake(600, 400));
    CCLayer *layer = CCLayer::create();
    scroll->setContainer(layer);

    WMMenu *menu = WMMenu::create(CCRectMake(scroll->getPosition().x, scroll->getPosition().y, 600, 400));
    menu->setPosition(CCPointZero);
    int heigth = 0;
    for(int i=0;i<40;i++)
    {
        CCMenuItemImage *image = CCMenuItemImage::create("Icon-72.png", "Icon-72.png", this, menu_selector(CCTextFieldTTFTest::buttonClick));
        image->setPosition(ccp(300, heigth));
        menu->addChild(image);
        
        heigth += 120;
    }
    
    layer->addChild(menu);
    layer->setContentSize(CCSizeMake(600, heigth));
    scroll->setContentSize(CCSizeMake(600, heigth));
    scroll->setDirection(kCCScrollViewDirectionVertical);
    
    addChild(scroll);

效果图如下

CCMenu超过CCScrollView边框还能点击处理,以及拖动CCMenuItem不响应点击事件_第1张图片


你可能感兴趣的:(cocos2d-x,CCMenu,触摸优先级)