最近做项目,遇到了这个问题,相信大家也会有很多遇见的,那就是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);
}
enum {
//* priority used by the menu for the event handler
kCCMenuHandlerPriority = -128,
};
既然知道了是因为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);