解决TableView / ScrollView上的Menu问题
1划出区域还可点击
重写CCMenu的触摸事件函数 TouchBegin/TouchMove/TouchCancle/TouchEnd
如果点击超出了 TableView/ScrollView边界则 TouchBegin返回false
2导致View不能滑动
透传CCMenu的触摸吞噬、让触摸可以下传,然后再touchMove中增加一个触摸滑动校验、如果触摸移动大于某个值(比如16),那么CCMenu则丢弃该触摸、不让menuItem执行activate,那么滑动的时候view上的menu就不会响应了。
也可以自己写个新的view,把里面的menu换成sprite,自己判断点击的位置,然后就知道点击的是哪一个了。相比之下重写menu工作量小多了、、、
// FXScrollMenu.h #pragma once #include "cocos2d.h" using namespace cocos2d; class FXScrollMenu : public cocos2d::CCMenu { public: //scrollVeiw/tabelView左下角世界坐标,view大小,menu超过view边界的部分就不可点击 //menu不会吞噬触摸消息,滑动时不响应消息 static FXScrollMenu* create(cocos2d::CCPoint viewLeftDownPos_worldCoordinate, cocos2d::CCSize viewAreaSize); bool init(cocos2d::CCPoint viewLeftDownPos_worldCoordinate, cocos2d::CCSize viewAreaSize); virtual void registerWithTouchDispatcher(); /** @brief For phone event handle functions */ virtual bool ccTouchBegan(cocos2d::CCTouch* touch, cocos2d::CCEvent* event); virtual void ccTouchEnded(cocos2d::CCTouch* touch, cocos2d::CCEvent* event); virtual void ccTouchCancelled(cocos2d::CCTouch *touch, cocos2d::CCEvent* event); virtual void ccTouchMoved(cocos2d::CCTouch* touch, cocos2d::CCEvent* event); protected: CCSize mViewSize; CCPoint mViewLeftDownPos; CCRect mViewRect; CCPoint mTouchStartPos; bool mTouchMoved; };
//FXScrollMenu.cpp #include "FXScrollMenu.h" //(点击校验范围) #define ViewTouchMove_Delta 16 FXScrollMenu* FXScrollMenu::create(cocos2d::CCPoint viewLeftDownPos_worldCoordinate, cocos2d::CCSize viewAreaSize) { FXScrollMenu *menu = new FXScrollMenu; if (menu && menu->init(viewLeftDownPos_worldCoordinate, viewAreaSize)) { menu->autorelease(); } else { CC_SAFE_DELETE(menu); menu = NULL; } return menu; } bool FXScrollMenu::init(cocos2d::CCPoint viewLeftDownPos_worldCoordinate, cocos2d::CCSize viewAreaSize) { if ( ! CCMenu::init()) return false; mViewLeftDownPos = viewLeftDownPos_worldCoordinate; mViewSize = viewAreaSize; mViewRect.setRect(mViewLeftDownPos.x, mViewLeftDownPos.y, mViewSize.width, mViewSize.height); return true; } void FXScrollMenu::registerWithTouchDispatcher() { CCDirector* pDirector = CCDirector::sharedDirector(); pDirector->getTouchDispatcher()->addTargetedDelegate(this, this->getTouchPriority(), false); } bool FXScrollMenu::ccTouchBegan(CCTouch* touch, CCEvent* event) { CCLog("FXScrollMenu : %s ", __FUNCTION__); CC_UNUSED_PARAM(event); // if (m_eState != kCCMenuStateWaiting || ! m_bVisible || !m_bEnabled) //by fx if (m_eState != kCCMenuStateWaiting || ! m_bVisible || ! isEnabled()) { return false; } for (CCNode *c = this->m_pParent; c != NULL; c = c->getParent()) { if (c->isVisible() == false) { return false; } } mTouchStartPos = touch->getLocation(); // by fx add if (mViewRect.containsPoint(mTouchStartPos)) // by fx add { mTouchMoved = false; // by fx add m_pSelectedItem = this->itemForTouch(touch); if (m_pSelectedItem) { m_eState = kCCMenuStateTrackingTouch; m_pSelectedItem->selected(); return true; } } return false; } void FXScrollMenu::ccTouchMoved(CCTouch* touch, CCEvent* event) { // if (mTouchMoved) return; // CC_UNUSED_PARAM(event); CCAssert(m_eState == kCCMenuStateTrackingTouch, "[Menu ccTouchMoved] -- invalid state"); CCMenuItem *currentItem = this->itemForTouch(touch); //add //移动了、那么该按钮不再响应点击消息了 CCPoint movePos = touch->getLocation(); if (fabs(movePos.x - mTouchStartPos.x) > ViewTouchMove_Delta || fabs(movePos.y - mTouchStartPos.y) > ViewTouchMove_Delta) { if (m_pSelectedItem) { m_pSelectedItem->unselected(); } mTouchMoved = true; return; } // if (currentItem != m_pSelectedItem) { if (m_pSelectedItem) { m_pSelectedItem->unselected(); } m_pSelectedItem = currentItem; if (m_pSelectedItem) { m_pSelectedItem->selected(); } } } void FXScrollMenu::ccTouchEnded(CCTouch *touch, CCEvent* event) { CC_UNUSED_PARAM(touch); CC_UNUSED_PARAM(event); CCAssert(m_eState == kCCMenuStateTrackingTouch, "[Menu ccTouchEnded] -- invalid state"); // if (m_pSelectedItem) if (m_pSelectedItem && ! mTouchMoved) { m_pSelectedItem->unselected(); m_pSelectedItem->activate(); } m_eState = kCCMenuStateWaiting; } void FXScrollMenu::ccTouchCancelled(CCTouch *touch, CCEvent* event) { CC_UNUSED_PARAM(touch); CC_UNUSED_PARAM(event); CCAssert(m_eState == kCCMenuStateTrackingTouch, "[Menu ccTouchCancelled] -- invalid state"); // if (m_pSelectedItem) if (m_pSelectedItem && ! mTouchMoved) { m_pSelectedItem->unselected(); } m_eState = kCCMenuStateWaiting; }
——————————————————————————————————————————
华丽的分割线
——————————————————————————————————————————
FXScrollMenu注册触摸消息时,设置的是不吞噬触摸,那么在点击按钮后,按钮响应了消息,如果TableCell也会相应touch消息,那么会触发两个事件,但此时我不希望tableCell也被触发,所以最好还是把FXScrollMenu注册为吞噬触摸。
那么此时如果只是想滑动界面,却点击到了menu不就滑不动了么,--->解决方法:在touchMove中校验得知是滑动view后,将该CCTouch保存下来,并发送出去CCDirector::sharedDirector()->getTouchDispatcher()->touchesBegan()(祥见另外一篇博文:触摸派发原理),然后在FXScrollMenu的touchBegan中判断如果是保存的menu则return false。
void FXScrollMenu::ccTouchMoved(CCTouch* touch, CCEvent* event) { // if (mTouchMoved) return; // CC_UNUSED_PARAM(event); CCAssert(m_eState == kCCMenuStateTrackingTouch, "[Menu ccTouchMoved] -- invalid state"); CCMenuItem *currentItem = this->itemForTouch(touch); //add //移动了、那么该按钮不再响应点击消息了 CCPoint movePos = touch->getLocation(); if (fabs(movePos.x - mTouchStartPos.x) > ViewTouchMove_Delta || fabs(movePos.y - mTouchStartPos.y) > ViewTouchMove_Delta) { if (m_pSelectedItem) { m_pSelectedItem->unselected(); } mTouchMoved = true; CCTargetedTouchHandler* pHandler = dynamic_cast<CCTargetedTouchHandler*>( CCDirector::sharedDirector()->getTouchDispatcher()->findHandler(this)); if (pHandler) { //把自己的touch移除、避免后面响应touchMove CCSet* mySet = pHandler->getClaimedTouches(); mySet->removeObject(touch); m_eState = kCCMenuStateWaiting; //然后重新派发出一个 触摸消息给低优先级的(该touch已被吞噬) mpTouch = touch; CCSet* _set = CCSet::create(); _set->addObject(mpTouch); CCDirector::sharedDirector()->getTouchDispatcher()->touchesBegan(_set, NULL); } return; } // if (currentItem != m_pSelectedItem) { if (m_pSelectedItem) { m_pSelectedItem->unselected(); } m_pSelectedItem = currentItem; if (m_pSelectedItem) { m_pSelectedItem->selected(); } } }
bool FXScrollMenu::ccTouchBegan(CCTouch* touch, CCEvent* event) { //是否是重新派发的 if (touch == mpTouch) { mpTouch = NULL; return false; } // CCLog("FXScrollMenu : %s ", __FUNCTION__); CC_UNUSED_PARAM(event); // if (m_eState != kCCMenuStateWaiting || ! m_bVisible || !m_bEnabled) //by fx if (m_eState != kCCMenuStateWaiting || ! m_bVisible || ! isEnabled()) { return false; } for (CCNode *c = this->m_pParent; c != NULL; c = c->getParent()) { if (c->isVisible() == false) { return false; } } mTouchStartPos = touch->getLocation(); // by fx add if (mViewRect.containsPoint(mTouchStartPos)) // by fx add { mTouchMoved = false; // by fx add m_pSelectedItem = this->itemForTouch(touch); if (m_pSelectedItem) { m_eState = kCCMenuStateTrackingTouch; m_pSelectedItem->selected(); return true; } } return false; }