****************************************************************************
时间:2015-01-26
作者:Sharing_Li
转载出处:http://www.jb51.cc/article/p-ostkmqhm-rx.html
****************************************************************************
在游戏开发当中,我们可能有比较特殊的需求,比如今天要讲解的,做一个具有游戏特色的简单的编辑输入框。还是老规矩,先看一下效果图吧,这里有三张,因为输入的键盘的弹出方式有三种,这里根据每种情况对其功能需求做一下简要说明:
首先是一般的形式:
(画面太美,不忍直视。。。)
功能需求:
1、只有点击输入框才弹出键盘;
4、点击确定按钮,或点击除了输入框和键盘的地方,键盘消失的简单动画。
有的时候,键盘弹出来会挡住输入框,看着有点不爽,所以我们可以如下方案:
功能需求:
1、和前一个相比,其实就是改变了键盘的弹出方式,其实也没改变,就是让原来不动的背景图也跟着一起动。
可是这种显示方案也不足,要是输入框所处的位置比较便上,那么背景图上移的时候,很可能就看不到输入框了。那么,问题来了,学挖掘机........键盘弹出方案哪家强?(打字打顺了~。~),想必大部分人都知道了,来看看最后一种显示:
功能需求:
1、和前面的相比,变成了全屏输入,多加了一个阴影层和输入框。
PS:1、补充下,那个键盘要适配屏幕的大小。
2、我们可以将键盘上的按钮图片换成具有自己游戏中的元素的图片
接下来,来看看代码的大致实现:
先浏览下头文件:
#ifndef _COLOR_EDIT_H_ #define _COLOR_EDIT_H_ #include "cocos2d.h" #include "cocos-ext.h" USING_NS_CC; USING_NS_CC_EXT; enum EditType { EditType_No = 0,Edit_Number,Edit_Alphabet,Edit_PinYin }; enum EditLocation { EditLocation_No = 0,Location_Down,Location_Nature,Location_Screen }; enum KeyBtn { Key_Num_0 = 0,Key_Num_1,Key_Num_2,Key_Num_3,Key_Num_4,Key_Num_5,Key_Num_6,Key_Num_7,Key_Num_8,Key_Num_9,Key_Delete,Key_Sure }; class ColorEdit : public cocos2d::Layer { public: ~ColorEdit(); ColorEdit(); static ColorEdit * create(const Size & size,const char * BgFile,Node * parent,EditLocation editLocation,EditType editType = Edit_Number); static ColorEdit * create(const Size & size,Scale9Sprite * pBgSprite,EditType editType = Edit_Number); bool myInit(Scale9Sprite * pBgSprite,EditType editType); protected: virtual bool onTouchBegan(Touch* touch,Event* pEvent); void onNumBtnCallback(Ref * obj); void onFunBtnCallback(Ref * obj); int getMaxZOrder(Node * node); void moveAction(bool isShow); void updateText(); private: Scale9Sprite * m_pEditBg; Sprite * m_pKeyBoard; EditType m_editType; EditLocation m_editLocation; Sprite * m_keyBg; Node * m_pTarget; bool m_isKeyShow; std::string m_text; }; #endif
再来具体看看实现部分:
首先初始化:
bool ColorEdit::myInit(Scale9Sprite * pBgSprite,EditType editType) { if (!Layer::init()) { return false; } m_pEditBg = pBgSprite; m_pTarget = parent; m_editLocation = editLocation; m_editType = editType; this->addChild(m_pEditBg); auto centerSize = m_pEditBg->getContentSize(); //这里要设置好九宫图中间的矩形的大小,因为我们要把输入的内容显示在上面,否则会看起来乱七八糟 m_pEditBg->setCapInsets(Rect(0,centerSize.width * 0.9,centerSize.height * 0.9)); auto pLabel = Label::createWithTTF("","fonts/Marker Felt.ttf",centerSize.height * 0.75); pLabel->setColor(Color3B::WHITE); pLabel->setAnchorPoint(Vec2(0,0.5)); pLabel->setTag(103); pLabel->setPosition(Vec2(centerSize.width * 0.08,centerSize.height * 0.5)); m_pEditBg->addChild(pLabel,2); //如果是全屏显示类型 if (m_editLocation == Location_Screen) { //添加阴影层,先隐藏 auto keyLayer = LayerColor::create(Color4B(0,100)); keyLayer->setPosition(Point::ZERO); keyLayer->setTag(100); m_pTarget->addChild(keyLayer,this->getMaxZOrder(parent) + 2); keyLayer->setVisible(false); //添加上方的输入框 auto key_bigbg = Scale9Sprite::create("coloredit/key_bigbg.png"); key_bigbg->setContentSize(Size(Director::getInstance()->getWinSize().width,key_bigbg->getContentSize().height)); key_bigbg->setTag(101); key_bigbg->setAnchorPoint(Vec2(0.5,0)); auto upSize = key_bigbg->getContentSize(); key_bigbg->setPosition(Vec2(m_pTarget->getContentSize().width / 2,m_pTarget->getContentSize().height)); m_pTarget->addChild(key_bigbg,this->getMaxZOrder(parent) + 2); //同样设置九宫图中间的矩形 key_bigbg->setCapInsets(Rect(0,upSize.width * 0.9,upSize.height * 0.9)); auto label_up = Label::createWithTTF("",upSize.height * 0.75); label_up->setColor(Color3B::WHITE); label_up->setAnchorPoint(Vec2(0,0.5)); label_up->setTag(102); label_up->setPosition(Vec2(upSize.width * 0.08,upSize.height / 2)); key_bigbg->addChild(label_up); } m_keyBg = Sprite::create("coloredit/key_bg.png"); m_keyBg->setAnchorPoint(Vec2(0.5,1)); m_keyBg->setPosition(Vec2(m_pTarget->getContentSize().width / 2,0)); m_pTarget->addChild(m_keyBg,this->getMaxZOrder(parent) + 2); auto bgSize = m_keyBg->getContentSize(); auto pMenu = Menu::create(); pMenu->setPosition(Vec2::ZERO); m_keyBg->addChild(pMenu); if (m_editType == Edit_Number)//添加数字键盘 { for (int i = 0; i < 2; i++) { for (int j = 0; j < 5; j++) { auto numSprNor = Sprite::create(__String::createWithFormat("coloredit/num_%d.png",i * 5 + j)->getCString()); auto numSprSel = Sprite::create(__String::createWithFormat("coloredit/num_%d.png",i * 5 + j)->getCString()); auto numBtn = MenuItemSprite::create(numSprNor,numSprSel,CC_CALLBACK_1(ColorEdit::onNumBtnCallback,this)); numBtn->setTag(Key_Num_0 + i * 5 + j); numBtn->setPosition(Vec2(bgSize.width / 10 * (j * 2 + 1),bgSize.height / 6 * ((3 - i) * 2 - 1))); pMenu->addChild(numBtn); } } auto delSprNor = Sprite::create("coloredit/btn_del.png"); auto delSprSel = Sprite::create("coloredit/btn_del.png"); auto delBtn = MenuItemSprite::create(delSprNor,delSprSel,CC_CALLBACK_1(ColorEdit::onFunBtnCallback,this)); delBtn->setTag(Key_Delete); delBtn->setPosition(Vec2(bgSize.width / 4 - 15,bgSize.height / 6)); pMenu->addChild(delBtn); auto sureSprNor = Sprite::create("coloredit/btn_sure.png"); auto sureSprSel = Sprite::create("coloredit/btn_sure.png"); auto sureBtn = MenuItemSprite::create(sureSprNor,sureSprSel,this)); sureBtn->setTag(Key_Sure); sureBtn->setPosition(Vec2(bgSize.width / 4 * 3 + 15,bgSize.height / 6)); pMenu->addChild(sureBtn); } else if (m_editType == Edit_Alphabet)//和number差不多,多了些按钮而已 { } else if (m_editType == Edit_PinYin)//这个要是实现的话,就成输入法了,咱们还是调用系统的吧。 { } //简单暴力的屏幕适配 auto rate_x = Director::getInstance()->getWinSize().width / bgSize.width; m_keyBg->setScaleX(rate_x); auto listenerT = EventListenerTouchOneByOne::create(); listenerT->onTouchBegan = CC_CALLBACK_2(ColorEdit::onTouchBegan,this); listenerT->setSwallowTouches(false); Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraPHPriority(listenerT,this); return true; }
然后是触摸函数的实现:
bool ColorEdit::onTouchBegan(Touch* touch,Event* pEvent) { auto touchPoint = touch->getLocation(); //如果是全屏显示类型,并且键盘已弹出 if (m_editLocation == Location_Screen && m_isKeyShow) { auto key_upEdit = (Scale9Sprite *)m_pTarget->getChildByTag(101); //如果点击除了顶层输入框和键盘的其他地方,则键盘消失 if (!m_keyBg->getBoundingBox().containsPoint(touchPoint) && !key_upEdit->getBoundingBox().containsPoint(touchPoint)) { this->moveAction(false); return true; } return false; } //将触摸点转为在当前子层下的坐标 touchPoint = this->convertToNodeSpace(touchPoint); if (!m_pEditBg->getBoundingBox().containsPoint(touchPoint)) { if (m_isKeyShow) { if (!m_keyBg->getBoundingBox().containsPoint(Vec2(touch->getLocation().x,touch->getLocation().y - (m_editLocation == Location_Down ? 1 : 0) * m_keyBg->getContentSize().height))) { this->moveAction(false); } } return false; } //如果点击了输入框,并且键盘还未弹出 if (!m_isKeyShow) { this->updateText(); this->moveAction(true); } return true; }
这里有个要注意的地方:
if (!m_keyBg->getBoundingBox().containsPoint(Vec2(touch->getLocation().x,touch->getLocation().y - (m_editLocation == Location_Down ? 1 : 0) * m_keyBg->getContentSize().height))) { this->moveAction(false); }
当键盘的弹出方式是效果图第二张时,因为背景图向上移动了一段距离,touch->getLocation()的y坐标要做一下修改,否则getBoundingBox的判断不正确。
void ColorEdit::onNumBtnCallback(Ref * obj) { int tag = ((Node *)obj)->getTag(); //点击按钮的简单动画 ((MenuItemSprite *)obj)->runAction(Sequence::create(ScaleTo::create(0.1,10 / 8.0),ScaleTo::create(0.1,1),NULL)); char temp[3]; sprintf(temp,"%d",tag); m_text += temp; //更新内容 this->updateText(); log("keyboard------->%d",tag); } void ColorEdit::onFunBtnCallback(Ref * obj) { ((MenuItemSprite *)obj)->runAction(Sequence::create(ScaleTo::create(0.1,NULL)); if (((Node *)obj)->getTag() == Key_Sure) { this->moveAction(false); } else { auto n = m_text.size(); if (n > 0) { m_text = m_text.substr(0,n - 1); } this->updateText(); } }
接着是键盘的出现与消失:
void ColorEdit::moveAction(bool isShow) { if (m_editLocation == Location_Screen) { auto keyLayer = (LayerColor *)m_pTarget->getChildByTag(100); auto key_upEdit = (Scale9Sprite *)m_pTarget->getChildByTag(101); keyLayer->setVisible(isShow); key_upEdit->runAction(MoveBy::create(0.35,Vec2(0,(isShow ? -1 : 1) * key_upEdit->getContentSize().height))); m_keyBg->runAction(MoveBy::create(0.35,(isShow ? 1 : -1) * m_keyBg->getContentSize().height))); } else if (m_editLocation == Location_Down) { Director::getInstance()->getRunningScene()->runAction(MoveBy::create(0.35,(isShow ? 1 : -1) * m_keyBg->getContentSize().height))); } else if (m_editLocation == Location_Nature) { m_keyBg->runAction(MoveBy::create(0.35,(isShow ? 1 : -1) * m_keyBg->getContentSize().height))); } m_isKeyShow = isShow; if (!isShow) { m_text = ""; } }
剩下的其他函数:
//获取最大的Zorder int ColorEdit::getMaxZOrder(Node * node) { int zorder = -1; Vector<Node *> nodeVec = node->getChildren(); for (auto node : nodeVec) { int temp = node->getLocalZOrder(); if (temp > zorder) { zorder = temp; } } log("children's max zorder is %d",zorder); return zorder; } void ColorEdit::updateText() { if (m_editLocation == Location_Screen) { ((Label *)((Scale9Sprite *)(m_pTarget->getChildByTag(101)))->getChildByTag(102))->setString(m_text); } else { ((Label *)(m_pEditBg->getChildByTag(103)))->setString(m_text); } }
最后我们在使用的时候,只需几行代码就行了。
auto colorEdit = ColorEdit::create(Size(250,40),"coloredit/input_Box.png",this,Location_Nature);
colorEdit->setPosition(Vec2(bgSize.width / 2,bgSize.height / 2));
bg->addChild(colorEdit);
到这里基本上说完了,写的不好的地方还请见谅。