解决TableView / ScrollView上的Menu问题(1滑出View区域还可点击2导致点击menu后View不能滑动)

 

解决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;
}

  

 

posted @ 2014-03-21 15:44  、拂晓  阅读(1171)  评论(1编辑  收藏  举报