这个需求是这样的
有一个场景类GameScene。这个类要addchild一个模态层(弹出层)的layer。模态层中有一个或几个按钮。
我现在不想(或是不能)把模态层中某一个sprite的点击事件写到模态层这个类中,而是要写到GameScene类中。如何实现呢?
看到网上一篇PopupLayer实现的例子。里面有一个方法,其中最重要的一句话是这个
void PopupLayer::buttonCallBack(Ref* pSender){
Node* node = dynamic_cast<Node*>(pSender);
CCLog("【====PopupLayer::buttonCallBack====】touch tag: %d", node->getTag());
if (m_callback && m_callbackListener){
(m_callbackListener->*m_callback)(node);
}
this->removeFromParent();
}
说心里话。对C++现在还不是很熟。这个
(m_callbackListener->*m_callback)(node);
我研究了半天也没弄明白是个什么原理。当然按照他的例子写是可以正常运行的。不过我希望我的每一行代码都可以知其然并且知其所以然。于是我毅然决然的换了另外一种实现方式。即委托。
其实C++本身是不像OC那样有委托的。不过好在我们可以很方便的委婉的实现这一机制。
首先新建一个委托类PopupNumberDelegate.h
#ifndef __POPUP_NUMBER_DELEGATE_H__
#define __POPUP_NUMBER_DELEGATE_H__
//popupnumber模态框委托
class PopupNumberDelegate
{
public:
virtual void spriteOnTouchEnded() = 0;
};
#endif // __POPUP_NUMBER_DELEGATE_H__
这个委托类里面只有一个纯虚方法。即spriteOnTouchEnded。我们GameScene类中最后要实现这个委托方法。
整个逻辑就是,模态层中的sprite点击后,会触发spriteOnTouchEnded这个委托方法。然后我们在GameScene中实现这个委托方法即可。
接下来是模态层
PopupNumberLayer.h
#ifndef __POPUP_NUMBER_LAYER_H__
#define __POPUP_NUMBER_LAYER_H__
#include "cocos2d.h"
#include "extensions/cocos-ext.h"
#include "PopupNumberDelegate.h"
USING_NS_CC;
USING_NS_CC_EXT;
class PopupNumberLayer : public LayerColor{
public:
virtual bool init();
CREATE_FUNC(PopupNumberLayer);
static PopupNumberLayer* createLayer();
PopupNumberDelegate* _delegate;
private:
Size visibleSize;
Vec2 origin;
//touch事件监听 屏蔽向下触摸
bool onTouchBegan(Touch *touch, Event *event);
void onTouchMoved(Touch *touch, Event *event);
void onTouchEnded(Touch* touch, Event* event);
bool onSpriteTouchBegan(Touch* touch, Event* event);
void onSpriteTouchEnded(Touch* touch, Event* event);
};
#endif // __POPUP_NUMBER_LAYER_H__
PopupNumberLayer.cpp
#include "PopupNumberLayer.h"
bool PopupNumberLayer::init(){
if (!LayerColor::init()){
return false;
}
visibleSize = Director::getInstance()->getVisibleSize();
origin = Director::getInstance()->getVisibleOrigin();
setColor(ccc3(0, 0, 0));
setOpacity(128);
//add layer touch event
auto mylistener = EventListenerTouchOneByOne::create();
mylistener->setSwallowTouches(true);
mylistener->onTouchBegan = CC_CALLBACK_2(PopupNumberLayer::onTouchBegan, this);
mylistener->onTouchMoved = CC_CALLBACK_2(PopupNumberLayer::onTouchMoved, this);
mylistener->onTouchEnded = CC_CALLBACK_2(PopupNumberLayer::onTouchEnded, this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(mylistener, this);
auto sprite = Sprite::createWithSpriteFrameName("block_3.png");
sprite->setPosition(Vec2(visibleSize / 2));
this->addChild(sprite);
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = CC_CALLBACK_2(PopupNumberLayer::onSpriteTouchBegan, this);
listener->onTouchEnded = CC_CALLBACK_2(PopupNumberLayer::onSpriteTouchEnded, this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, sprite);
return true;
}
PopupNumberLayer* PopupNumberLayer::createLayer()
{
auto layerColor = PopupNumberLayer::create();
return layerColor;
}
bool PopupNumberLayer::onSpriteTouchBegan(Touch* touch, Event* event)
{
// 获取事件所绑定的 target
auto target = static_cast<Sprite*>(event->getCurrentTarget());
// 获取当前点击点所在相对按钮的位置坐标
Point locationInNode = target->convertToNodeSpace(touch->getLocation());
Size s = target->getContentSize();
Rect rect = Rect(0, 0, s.width, s.height);
// 点击范围判断检测
if (rect.containsPoint(locationInNode))
{
log("sprite 坐标 ... x = %f, y = %f", locationInNode.x, locationInNode.y);
target->setOpacity(180);
return true;
}
return false;
}
void PopupNumberLayer::onSpriteTouchEnded(Touch* touch, Event* event)
{
this->_delegate->spriteOnTouchEnded();
}
bool PopupNumberLayer::onTouchBegan(Touch *touch, Event *event){
return true;
}
void PopupNumberLayer::onTouchMoved(Touch *touch, Event *event){
}
void PopupNumberLayer::onTouchEnded(Touch* touch, Event* event){
}
所有代码都很简单。这里最重要的就是一个地方。
点击sprite并且松开时候,会触发onSpriteTouchEnded方法。在onSpriteTouchEnded方法中又这么一句话this->_delegate->spriteOnTouchEnded();
同时在.h的声明中声明了这个变量PopupNumberDelegate* _delegate;
这里就是重点。即我们点击sprite并且松开后,会执行PopupNumberDelegate类中的spriteOnTouchEnded()方法。
那么spriteOnTouchEnded方法我们写的是一个纯虚方法。接下来怎么办呢?很简单。GameScene继承PopupNumberDelegate类,并且实现这个虚方法,就达到了我们的目的。接下来看代码,我只保留我们最需要关注的一部分代码
GameScene.h
#ifndef __GAME_SCENE_H__
#define __GAME_SCENE_H__
#include "cocos2d.h"
#include "PopupNumberLayer.h"
#include "PopupNumberDelegate.h"
USING_NS_CC;
class GameScene : public cocos2d::Layer, public PopupNumberDelegate
{
public:
static Scene* createScene();
virtual bool init();
virtual void spriteOnTouchEnded();
CREATE_FUNC(GameScene);
private:
Size visibleSize;
Vec2 origin;
PopupNumberLayer* _popupNumberLayer;
};
#endif // __GAME_SCENE_H__
GameScene.cpp
#include "GameScene.h"
Scene* GameScene::createScene()
{
auto scene = Scene::create();
auto layer = GameScene::create();
scene->addChild(layer);
return scene;
}
bool GameScene::init()
{
if (!Layer::init())
{
return false;
}
visibleSize = Director::getInstance()->getVisibleSize();
origin = Director::getInstance()->getVisibleOrigin();
//添加模态框
_popupNumberLayer = PopupNumberLayer::createLayer();
_popupNumberLayer->_delegate = this;
this->addChild(_popupNumberLayer);
return true;
}
void GameScene::spriteOnTouchEnded()
{
CCLOG("点击了sprite");
}
这时候运行项目。会出现一个模态框。点击模态框中的sprite后,会依次显示“sprite 坐标 ... x = xxx, y = xxx”,"点击了sprite"。
完美解决