【cocos2dx】监听安卓机的返回键

2016-02-19新增-------------------------------------------------------------------------------------------------------------

--quick-cocos2d-x_2.2.6

--返回按键监听
self.layer = display.newLayer()
self.layer:addNodeEventListener(cc.KEYPAD_EVENT, function(event)  
	if event.key == "back" then  
		--self.mainPopup:show("要退出吗", true)
		--再按一次返回键退出
	end  
end)
self.layer:setKeypadEnabled(true)
self:addChild(self.layer)

原文-----------------------------------------------------------------------------------------------------------------------------

(Cocos-2.2.6 & Framework-3.5)

在玩cocos2d-x的时候,每次真机实测,都要按home键来‘退出’游戏,因为没有监听返回键,这让我想起了某个版本的手机QQ,也是没有监听返回键来着。

来简单写一下cocos2d-x监听安卓的返回键。

 

简单的用helloworld项目来实验

首先在.h文件覆盖onKeyReleased函数。

	virtual void onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event);

在.cpp中init函数里面注册键盘监听

bool HelloWorld::init()
{
//省略一些代码
	auto backKeyListener = EventListenerKeyboard::create();
	backKeyListener->onKeyReleased = CC_CALLBACK_2(HelloWorld::onKeyReleased, this);
	_eventDispatcher->addEventListenerWithSceneGraphPriority(backKeyListener, this);
}

之后捕捉返回键(不排除手机机型问题和引擎版本问题引起keyCode的不同,keyCode请自行真机测试,我这里三星S4的返回键对应KEY_BACK)

void HelloWorld::onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event)
{
	if (keyCode == EventKeyboard::KeyCode::KEY_BACK)
	{
		Director::getInstance()->end();
	}
}

好了,很简单。返回键按下立马退出。但是也太简单了,我们比较常见的app或者游戏的返回键监听一般设计成这两种形式:

1、“再按一次返回键退出”。2、弹出对话框确认退出。

 

先来第一种。

我们要实现“再按一次返回键退出”,那就得让程序知道,用户到底按了几次返回键。

另外,这两次按下返回键的间隔也应该有个限制,总不能,第一按下的时候,提示“再按一次返回键退出”,过了10分钟再按返回键就直接退出了。

相应的步骤大概是这样。

【cocos2dx】监听安卓机的返回键_第1张图片

在上一段的代码基础上,.h文件里面声明一个bool变量来标记是否按了第一次。

	bool m_keyBackFlag;

之后在init函数里面初始化为false

	m_keyBackFlag = false;

然后HelloWorld::onKeyReleased要改挺多,因为我们要区别对待是否按了第一次返回键,所以先套一个if(!m_keyBackFlag)else,之后再分别编写。

void HelloWorld::onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event)
{
	if (keyCode == EventKeyboard::KeyCode::KEY_BACK)
	{
		if (!m_keyBackFlag)// 在过去的1秒钟之内没按过返回键,提示信息再按一次
		{
			TTFConfig ttfConfig("fonts/jt.ttf", 24);
			auto pMessage = Label::createWithTTF(ttfConfig,
				"再按一次返回键", 
				TextHAlignment::LEFT, Director::getInstance()->getWinSize().width * 0.7);
			pMessage->setPosition(Point(100, 80));
			this->addChild(pMessage);

			auto pMessageFadeOut = FadeOut::create(1.0f);
			auto pMessageMoveBy = MoveBy::create(1.0f, Vec2(0, 75));
			auto pSpawn = Spawn::create(pMessageMoveBy, pMessageFadeOut, nullptr);
			pMessage->runAction(pSpawn);

			m_keyBackFlag = true;

			this->scheduleOnce(
				[=](float dt){
					this->m_keyBackFlag = false;
				},
				1,//1秒之内如果不按返回键,那么m_keyBackFlag变量重置
				"keyBackFlag"
			);
		}
		else//在过去的1秒钟之内曾按下返回键,现在按的是第二下
		{
			Director::getInstance()->end();
		}
	}
}
【cocos2dx】监听安卓机的返回键_第2张图片

效果图

细心的同学可能发现了,我们的左下角的信息里节点数在涨个不停。原因是,虽然这个提示信息(Label)消失了,但是我们没有移除它。

所以,每次显示的提示信息(Label)都会一直存在于我们的项目中。所以我们要移除它。也就是,在它消失的那一刻,他的生命是周期也结束了。

我们既然写有了一个lambda,那就好好利用它。

首先在pMessage->runAction(pSpawn);下加一句pMessage->retain();。为什么,后面再提。

之后在lambda里面的this->m_keyBackFlag = false;后面加一句this->removeChild(pMessage);。

刚刚前面要调用pMessage的retain函数,原因是,我们需要在一段时间后才移除这个pMessage指针,但是呢,那个时候程序已经离开了这个代码快。

pMessage指针失效了,pMessage指向的Label还在(透明的)。所以我们需要调用retain(),让它摆脱cocos的内存管理机制,来让lambda能捕捉到它。

再上图

【cocos2dx】监听安卓机的返回键_第3张图片

这回节点数就正常了。(因为只加了两行代码,请自行添加吧,我不贴代码了)

==========================================================================================

再看第二种,弹出确认框。

这里我先说一下,对话框的类我没有实现,只是用成员指针来模拟实现的。

弹出对话框的实现,bool变量的flag我还是留着的,防止按多次返回键来弹出创建多次对话框(因为我没有写对话框的类,只是当场创建精灵和按钮来模拟对话框的)

当然用别的方法来预防也是可以的。

相应的步骤大概是这样。

【cocos2dx】监听安卓机的返回键_第4张图片

这里要解决的一个小问题是,在弹出退出确认框的时候,游戏本身的所有监听事件就要屏蔽了,防止被玩家误触。也可以说,弹出来的对话框优先级最高。

我这里大概的思路就是,先做一个遮盖层,这个层吸收所有的触摸事件,然后在这个层之上,创建对话框。

如果用户选择退出,则退出游戏。如果取消,则返回游戏,移除这个遮盖层和对话框。

思路是这样,实现起来可以用层(Layer),我这里不用层(Layer)来实现这个遮盖用的层。

我们用一个足够大ui::ImageView对象,设置交互性setTouchEnabled(true);,这样,就能做到我们想要的效果了。

当然,也可以用其他的有setTouchEnable成员的类,只不过,ui::ImageView的创建已经非常简便了。就用这个好了。

之后,再创建对话框,并且设置好回调函数就好了。

因为要用成员变量来模拟对话框,所以成员变量比较多,如果自己已经实现了对话框类,那么只要少量成员变量就好了。

.h文件中声明的变量和函数。我们依然需要覆盖onKeyReleased函数。另外声明两个回调函数,分别作为确定退出和取消退出的回调。

先上效果图。

【cocos2dx】监听安卓机的返回键_第5张图片

对话框是用精灵和按钮模拟的,所以成员变量有点多,如果自己实现了对话框类的话,变量会少很多。

	virtual void onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event);
	void testCallback(Ref* pSender);
	void testCallback2(Ref* pSender);

	bool m_keyBackFlag;
	ui::ImageView* m_cover;
	Sprite* m_outBox;
	Label* m_pMessage2;
	Label* m_pLabelYes;
	Label* m_pLabelNo;
	MenuItemLabel* m_pItemYes;
	MenuItemLabel* m_pItemNo;
	Menu* m_pm;

那么初始化的时候可以全部赋空指针。

bool HelloWorld::init()
{
    //省略了一些代码
	m_cover = nullptr;
	m_pMessage2 = nullptr;
	m_pLabelYes = nullptr;
	m_pLabelNo = nullptr;
	m_pItemYes = nullptr;
	m_pItemNo = nullptr;
	m_pm = nullptr;
	m_outBox = nullptr;
	m_keyBackFlag = false;
	//同样还要注册监听
	auto backKeyListener = EventListenerKeyboard::create();
	backKeyListener->onKeyReleased = CC_CALLBACK_2(HelloWorld::onKeyReleased, this);
	_eventDispatcher->addEventListenerWithSceneGraphPriority(backKeyListener, this);
}

void HelloWorld::onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event)
{
	if (keyCode == EventKeyboard::KeyCode::KEY_BACK)
	{
		if (!m_keyBackFlag)
		{
			//遮挡层用ui::ImageView来吸收触屏事件
			m_cover = ui::ImageView::create("colorBG.png");
			m_cover->setPosition(Vec2(Director::getInstance()->getWinSize().width * 0.5, Director::getInstance()->getWinSize().height * 0.5));
			m_cover->setTouchEnabled(true);
			m_cover->setOpacity(128);
			this->addChild(m_cover);

			//下面都是对话框的模拟实现
			m_outBox = Sprite::create("outBox.png");
			m_outBox->setPosition(270, 450);
			this->addChild(m_outBox);

			TTFConfig ttfConfig("fonts/jt.ttf", 24);
			m_pMessage2 = Label::createWithTTF(ttfConfig,
				"确定要退出么?",
				TextHAlignment::LEFT, Director::getInstance()->getWinSize().width * 0.7);
			m_pMessage2->setPosition(Vec2(270, 500));
			this->addChild(m_pMessage2);

			m_pLabelYes = Label::createWithTTF(ttfConfig,
				"是的",
				TextHAlignment::LEFT, Director::getInstance()->getWinSize().width * 0.7);
			m_pLabelNo = Label::createWithTTF(ttfConfig,
				"不要",
				TextHAlignment::LEFT, Director::getInstance()->getWinSize().width * 0.7);
			m_pItemYes = MenuItemLabel::create(m_pLabelYes, CC_CALLBACK_1(HelloWorld::testCallback, this));
			m_pItemNo = MenuItemLabel::create(m_pLabelNo, CC_CALLBACK_1(HelloWorld::testCallback2, this));
			m_pItemYes->setPosition(Vec2(270, 450));
			m_pItemNo->setPosition(Vec2(270, 400));

			m_pm = Menu::create(m_pItemYes, m_pItemNo, nullptr);
			m_pm->setPosition(Vec2(0, 0));
			this->addChild(m_pm);
			
			m_keyBackFlag = true;
		}
	}
}

void HelloWorld::testCallback(Ref* pSender)//确定退出的回调
{
	Director::getInstance()->end();
}

void HelloWorld::testCallback2(Ref* pSender)//取消退出的回调
{//移除对话框
	CCLOG("testCallback2");
	this->m_keyBackFlag = false;
	this->removeChild(m_pMessage2);
	this->removeChild(m_pLabelYes);
	this->removeChild(m_pLabelNo);
	this->removeChild(m_pm);
	this->removeChild(m_pItemYes);
	this->removeChild(m_pItemNo);
	this->removeChild(m_outBox);
	this->removeChild(m_cover);
}


你可能感兴趣的:(cocos2d-x:小代码)