在开发游戏的时候我们经常需要在层与层之间、场景与场景之间传递数据和消息,Cocos2dx框架应用观察者模式为我们封装了一个CCNotificationCenter类,也叫消息通知中心,它也是一个单例类。
什么是观察者模式?看我的另一篇文章:设计模式C++实现——观察者模式
从观察者模式来讲,CCNotificationCenter类是观察者模式中的目标对象(主题),而CCNotificationObserver则是观察者。一个目标对象可以注册多个观察者,当目标对象的状态改变的时候,可以通知观察者对象作出相应的反应,这是标准的观察者模式的实现。但是CCNotificationCenter稍微有些许差别,CCNotificationCenter不是通过自身状态改变来通知观察者,而是通过显式地发送观察者感兴趣的消息来通知它们,消息名称则是用来标识观察者是否感兴趣。每次消息传递给CCNotificationCenter,CCNotificationCenter就会遍历所有的观察者,找到注册了该消息标识符的观察者,然后将消息发送给它们。
下面看一下这个类的API:
/** 获取CCNotificationCenter的单例类 */ static CCNotificationCenter *sharedNotificationCenter(void); /** 销毁CCNotificationCenter的单例类 */ static void purgeNotificationCenter(void); /** 将一个特定的对象添加为观察者 * target: 注册为观察者的对象 * selector: 当收到指定消息时的回调函数 * name: 消息名称,唯一标识符 * obj: 传递给回调函数的额外的参数 */ void addObserver(CCObject *target, SEL_CallFuncO selector, const char *name, CCObject *obj); /** 移除观察者 */ void removeObserver(CCObject *target,const char *name); /** 移除所有观察者 */ int removeAllObservers(CCObject *target); /** 发送不带数据的消息 */ void postNotification(const char *name); /** 发送带数据的消息 */ void postNotification(const char *name, CCObject *object);
上一篇说到使用CCTableView类实现了好友列表,然后我们需要创建一个好友详细信息的界面,当在列表中点击某个好友时,跳转到详细界面显示该好友的详细信息。这个时候就需要在两个层(或者说两个场景)之间传递数据,下面我们做一下小测试:
首先新建一个好友详细信息场景,并添加为观察者,以监听消息:
FriendDetailScene.h
#ifndef __FRIEND_DETAIL_SCENE_H__ #define __FRIEND_DETAIL_SCENE_H__ #include "cocos2d.h" #include "cocos-ext.h" class FriendDetail : public cocos2d::CCLayer { public: FriendDetail(); ~FriendDetail(); virtual bool init(); static cocos2d::CCScene* scene(); void backToPrior(CCObject* pSender); void getMessage(CCObject* obj); CREATE_FUNC(FriendDetail); }; #endif
FriendDetailScene.cpp
#include "FriendDetailScene.h" USING_NS_CC; USING_NS_CC_EXT; FriendDetail::FriendDetail() { } FriendDetail::~FriendDetail() { CCNotificationCenter::sharedNotificationCenter()->purgeNotificationCenter(); // 释放通知中心对象 } CCScene* FriendDetail::scene() { CCScene *scene = CCScene::create(); FriendDetail *layer = FriendDetail::create(); scene->addChild(layer); return scene; } bool FriendDetail::init() { if ( !CCLayer::init() ) { return false; } CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin(); // 背景 CCSprite* pSprite = CCSprite::create("IMG_Background.png"); pSprite->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y)); this->addChild(pSprite, 0); // 返回按钮 CCMenuItemImage *pBackItem = CCMenuItemImage::create( "button1.png", "button1.png", this, menu_selector(FriendDetail::backToPrior)); pBackItem->setPosition(680, 100); CCMenu* pMenu = CCMenu::create(pBackItem, NULL); pMenu->setPosition(CCPointZero); this->addChild(pMenu, 1); // 接收消息 CCNotificationCenter::sharedNotificationCenter()->addObserver(this, callfuncO_selector(FriendDetail::getMessage), "selected_cell", NULL); return true; } void FriendDetail::backToPrior(CCObject* pSender) { CCDirector::sharedDirector()->popScene(); } void FriendDetail::getMessage( CCObject* obj ) { // 头像icon路径 std::string imagePath[10] = {"icon0.png","icon1.png","icon2.png","icon3.png","icon4.png","icon5.png","icon6.png","icon7.png","icon8.png","icon9.png"}; CCTableViewCell* cell = static_cast<CCTableViewCell*>(obj); int idx = cell->getIdx(); CCSprite* iconSprite = CCSprite::create(imagePath[idx].c_str()); iconSprite->setPosition(ccp(400, 380)); this->addChild(iconSprite); }从代码中可以看出,我们在init()初始化时注册了观察者,监听名为“selected_cell”的消息,当收到这个消息时,就会执行getMessage( CCObeject* obj )函数。一般接收通知的一方在接收完通知后需要removeObserver(移除监听)或者purgeNotificationCenter(销毁通知中心)。
然后我们就可以在好友列表层FriendListLayer的tableCellTouched( CCTableView* table, CCTableViewCell* cell )添加发送消息的相关代码:
void FriendListLayer::tableCellTouched( CCTableView* table, CCTableViewCell* cell ) { //CCLOG("Cell touched at index: %i", cell->getIdx()+1); // 一定要先注册监听,然后发送消息,这样才可以实现数据的传递。 CCScene* pScene = FriendDetail::scene(); CCDirector::sharedDirector()->pushScene(pScene); CCNotificationCenter::sharedNotificationCenter()->postNotification("selected_cell", (CCObject*)cell); }这里是将被点击的单元格cell传递过去,当FriendDetailLayer接收到cell时可以获取它的idx,这样就实现了数据的传递。
注意:如果改成下面这样将无法传递数据...
void FriendListLayer::tableCellTouched( CCTableView* table, CCTableViewCell* cell ) { //CCLOG("Cell touched at index: %i", cell->getIdx()+1); CCNotificationCenter::sharedNotificationCenter()->postNotification("selected_cell", (CCObject*)cell); CCScene* pScene = FriendDetail::scene(); CCDirector::sharedDirector()->pushScene(pScene); }
因为添加监听(addObserver)和发送消息(postNotification)是有先后顺序的,一定要先注册监听,然后发送消息,这样才可以实现数据的传递。
测试效果: