下面簡單記錄一下如何Cocos2d-x中創(chuàng)建輸入編輯框。在引擎中為我們提供了這樣兩個類:CCEditBox 和 CCTextFieldTTF。 一、 CCEditBox ①這個類文件的位置
②這個類是繼承自 CCControlButton 和 CCIMEDelegate。其中的 CCIMEDelegate代理類中定義了四個代理方法,在使用的時候根據(jù)需要選擇實現(xiàn)相應(yīng)的委托方法,從方法名就可以大致知道是什么意思了。 class CCEditBoxDelegate { public: virtual ~CCEditBoxDelegate() {}; /** * This method is called when an edit box gains focus after keyboard is shown. * @param editBox The edit box object that generated the event. */ virtual void editBoxEditingDidBegin(CCEditBox* editBox) {}; /** * This method is called when an edit box loses focus after keyboard is hidden. * @param editBox The edit box object that generated the event. */ virtual void editBoxEditingDidEnd(CCEditBox* editBox) {}; /** * This method is called when the edit box text was changed. * @param editBox The edit box object that generated the event. * @param text The new text. */ virtual void editBoxTextChanged(CCEditBox* editBox, const std::string& text) {}; /** * This method is called when the return button was pressed or the outside area of keyboard was touched. * @param editBox The edit box object that generated the event. */ virtual void editBoxReturn(CCEditBox* editBox) = 0; }; 下面通過一個demo來使用一下 CCEditBox 這個類中的相關(guān)方法(通過查看該類的頭文件可知道更加詳細(xì)的方法介紹)。 注意:使用的時候需要: #include "cocos-ext.h" USING_NS_CC_EXT ; 頭文件: class HelloWorld : public cocos2d::CCLayer,public CCEditBoxDelegate { public: // Method 'init' in cocos2d-x returns bool, instead of 'id' in cocos2d-iphone (an object pointer) virtual bool init(); // there's no 'id' in cpp, so we recommend to return the class instance pointer static cocos2d::CCScene* scene(); // a selector callback void menuCloseCallback(CCObject* pSender); // preprocessor macro for "static create()" constructor ( node() deprecated ) CREATE_FUNC(HelloWorld); virtual void editBoxEditingDidBegin(cocos2d::extension::CCEditBox* editBox); virtual void editBoxEditingDidEnd(cocos2d::extension::CCEditBox* editBox); virtual void editBoxTextChanged(cocos2d::extension::CCEditBox* editBox, const std::string& text); virtual void editBoxReturn(cocos2d::extension::CCEditBox* editBox); private: CCEditBox* editBox; }; 實現(xiàn)文件: bool HelloWorld::init() { ////////////////////////////// // 1. super init first if ( !CCLayer::init() ) { return false; } CCSize winSize = CCDirector::sharedDirector()->getWinSize(); CCSize editSize = CCSizeMake(300, 50); //第一個size參數(shù)表示輸入編輯框的大小,第二個參數(shù)九宮格是用于輸入編輯框的背景 editBox = CCEditBox::create(editSize, CCScale9Sprite::create("12.png")); editBox->cocos2d::CCNode::setPosition(winSize.width/2, winSize.height-80); //以setFont開頭的有幾個方法是 用于設(shè)置輸入文字的字體,大小,顏色 editBox->setFontSize(25); editBox->setFontColor(ccRED); //設(shè)置輸入編輯框在還沒有輸入的時候默認(rèn)的提示文字 editBox->setPlaceHolder("Name: "); //同樣的,也有幾個對應(yīng)的方法的是用于設(shè)置這些提示文字的,都是以setPlaceHolder開頭的 editBox->setPlaceholderFontColor(ccWHITE); //設(shè)置輸入編輯文字的長度,一個字符為一個長度 editBox->setMaxLength(20); //設(shè)置鍵盤中return鍵顯示的字符 editBox->setReturnType(kKeyboardReturnTypeGo); //包括這些選項 // kKeyboardReturnTypeDefault: 默認(rèn)使用鍵盤return 類型 // kKeyboardReturnTypeDone: 默認(rèn)使用鍵盤return類型為“Done”字樣 // kKeyboardReturnTypeSend: 默認(rèn)使用鍵盤return類型為“Send”字樣 // kKeyboardReturnTypeSearch: 默認(rèn)使用鍵盤return類型為“Search”字樣 // kKeyboardReturnTypeGo: 默認(rèn)使用鍵盤return類型為“Go”字樣 //設(shè)置輸入編輯框的編輯類型 editBox->setInputMode(kEditBoxInputModeAny); //包括這些選項 // kEditBoxInputModeAny: 開啟任何文本的輸入鍵盤,包括換行 // kEditBoxInputModeEmailAddr: 開啟 郵件地址 輸入類型鍵盤 // kEditBoxInputModeNumeric: 開啟 數(shù)字符號 輸入類型鍵盤 // kEditBoxInputModePhoneNumber: 開啟 電話號碼 輸入類型鍵盤 // kEditBoxInputModeUrl: 開啟 URL 輸入類型鍵盤 // kEditBoxInputModeDecimal: 開啟 數(shù)字 輸入類型鍵盤,允許小數(shù)點 // kEditBoxInputModeSingleLine: 開啟任何文本的輸入鍵盤,不包括換行 //設(shè)置該屬性輸入密碼時為替代符 // editBox->setInputFlag(kEditBoxInputFlagPassword); //如果只是簡單輸入字符,則不用這個設(shè)置 //包括這些選項 // kEditBoxInputFlagPassword, // kEditBoxInputFlagSensitive, // kEditBoxInputFlagInitialCapsWord, // kEditBoxInputFlagInitialCapsSentence, // kEditBoxInputFlagInitialCapsAllCharacters //設(shè)置委托代理對象為當(dāng)前類 editBox->setDelegate(this); this->addChild(editBox); return true; } 委托方法可以根據(jù)需要自定義實現(xiàn)相關(guān)的內(nèi)容。 void HelloWorld::editBoxEditingDidBegin(cocos2d::extension::CCEditBox *editBox) { } void HelloWorld::editBoxEditingDidEnd(cocos2d::extension::CCEditBox *editBox) { } void HelloWorld::editBoxTextChanged(cocos2d::extension::CCEditBox *editBox, const std::string &text) { } void HelloWorld::editBoxReturn(cocos2d::extension::CCEditBox *editBox) { CCLOG("the text = %s",editBox->getText()); }
下面有一點要提示:本人在使用過程中遇到過這樣一個問題,創(chuàng)建好對象添加到layer中的時候,點擊編輯框,沒有響應(yīng)(沒有調(diào)出鍵盤),debug了好一會才發(fā)現(xiàn),是觸摸層級的問題,后來修改了layer的觸摸等級(降低了layer的優(yōu)先級),就可以了。 二、 CCTextFieldTTF ①這個類文件的位置
libs/cocos2dx/text_input_node/ ②這個類是繼承了 CCLabelTTF 和 CCIMEDelegate 。 通過其繼承了 CCLabelTTF這個類和 CCIMEDelegate 鍵盤代理類。 根據(jù)其繼承自 CCLabelTTF, 我們就可以大致猜測到其實現(xiàn)了,估計就是一個動態(tài)的 CCLabelTTF ,通過不斷監(jiān)聽輸入的字符,動態(tài)設(shè)置 label 的字符,進(jìn)行顯示而已吧! 而其繼承 CCIMEDelegate 這個鍵盤代理類,其中的代理方法主要是處理鍵盤的出現(xiàn)和隱藏過程中的一些自定義過程實現(xiàn),如果需要,可以繼承這個類,并重寫其中的相關(guān)方法。
另外,在layer中使用
CCTextFieldTTF創(chuàng)建輸入編輯框?qū)ο蟮臅r候,需要繼承
CCTextFieldDelegate
這個代理類,其
中也定義了相關(guān)的委托方法,使用的時候根據(jù)需要選擇實現(xiàn)即可。
而這個 CCTextFieldDelegate 委托方法主要是處理在編輯輸入框鍵入字符過程中的一些自定義實現(xiàn)。下面就是這個代理類中的相關(guān)方法,看方法名就知道鍵入字符分成四個過程了。 class CC_DLL CCTextFieldDelegate { public: /** @brief If the sender doesn't want to attach to the IME, return true; */ virtual bool onTextFieldAttachWithIME(CCTextFieldTTF * sender) { CC_UNUSED_PARAM(sender); return false; } /** @brief If the sender doesn't want to detach from the IME, return true; */ virtual bool onTextFieldDetachWithIME(CCTextFieldTTF * sender) { CC_UNUSED_PARAM(sender); return false; } /** @brief If the sender doesn't want to insert the text, return true; */ virtual bool onTextFieldInsertText(CCTextFieldTTF * sender, const char * text, int nLen) { CC_UNUSED_PARAM(sender); CC_UNUSED_PARAM(text); CC_UNUSED_PARAM(nLen); return false; } /** @brief If the sender doesn't want to delete the delText, return true; */ virtual bool onTextFieldDeleteBackward(CCTextFieldTTF * sender, const char * delText, int nLen) { CC_UNUSED_PARAM(sender); CC_UNUSED_PARAM(delText); CC_UNUSED_PARAM(nLen); return false; } /** @brief If the sender doesn't want to draw, return true. */ virtual bool onDraw(CCTextFieldTTF * sender) { CC_UNUSED_PARAM(sender); return false; } }; 其中的方法 return true或者false要注意 ?。捶椒ㄇ懊娴淖⑨專?
下面通過testcpp中的一個 TextFieldTTFActionTest 來具體實踐一下(本人抽離出相關(guān)的代碼并做了相應(yīng)的補(bǔ)充修改) 簡單對程序說明一下:實現(xiàn)了鍵盤出現(xiàn)和隱藏時候的視圖內(nèi)容的自動調(diào)整,同時在輸入字符和 刪除字符 的時候有動作特效。 還有就是鍵盤的錨地位置是在其左下角,在鍵盤出現(xiàn)和隱藏方法中輸出的數(shù)值中就可以發(fā)現(xiàn)。 我的測試的過程中有一個特別坑的bug,就是在onEnter這個方法中,忘了 CCLayer :: onEnter (); 對父類的調(diào)用,所以導(dǎo)致程序莫名其妙的問題(無法執(zhí)行動作),找了好久,看到http://blog.csdn.net/somestill/article/details/9875743這篇文章后,才找出這個bug。
下面直接貼出代碼,看懂應(yīng)該沒有什么問題。
頭文件: #ifndef __HELLOWORLD_SCENE_H__ #define __HELLOWORLD_SCENE_H__ #include "cocos2d.h" USING_NS_CC; class HelloWorld : public cocos2d::CCLayer,public CCTextFieldDelegate,public CCIMEDelegate { public: // Method 'init' in cocos2d-x returns bool, instead of 'id' in cocos2d-iphone (an object pointer) virtual bool init(); // there's no 'id' in cpp, so we recommend to return the class instance pointer static cocos2d::CCScene* scene(); // preprocessor macro for "static create()" constructor ( node() deprecated ) CREATE_FUNC(HelloWorld); void callbackRemoveNodeWhenDidAction(CCNode * pNode); virtual void onClickTrackNode(bool bClicked); // CCLayer virtual void onEnter(); virtual void onExit(); virtual void registerWithTouchDispatcher(); virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent); virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent); // CCTextFieldDelegate virtual bool onTextFieldAttachWithIME(CCTextFieldTTF * pSender); virtual bool onTextFieldDetachWithIME(CCTextFieldTTF * pSender); virtual bool onTextFieldInsertText(CCTextFieldTTF * pSender, const char * text, int nLen); virtual bool onTextFieldDeleteBackward(CCTextFieldTTF * pSender, const char * delText, int nLen); virtual bool onDraw(CCTextFieldTTF * pSender); //CCIMEDelegate //keyboard show/hide notification virtual void keyboardWillShow(CCIMEKeyboardNotificationInfo& info); virtual void keyboardWillHide(CCIMEKeyboardNotificationInfo& info); private: CCTextFieldTTF* m_pTextField; CCAction* m_pTextFieldAction; bool m_bAction; int m_nCharLimit; // the textfield max char limit CCPoint m_beginPos; float adjustVert; }; #endif // __HELLOWORLD_SCENE_H__ 實現(xiàn)文件: #include "HelloWorldScene.h" #include "SimpleAudioEngine.h" using namespace cocos2d; using namespace CocosDenshion; #define FONT_NAME "Thonburi" #define FONT_SIZE 36 CCScene* HelloWorld::scene() { // 'scene' is an autorelease object CCScene *scene = CCScene::create(); // 'layer' is an autorelease object HelloWorld *layer = HelloWorld::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } // on "init" you need to initialize your instance bool HelloWorld::init() { ////////////////////////////// // 1. super init first if ( !CCLayer::init() ) { return false; } setTouchEnabled(true); //注意要設(shè)置當(dāng)前l(fā)ayer為可觸摸 CCSize size = CCDirector::sharedDirector()->getWinSize(); CCSprite* pSprite = CCSprite::create("HelloWorld.png"); pSprite->setPosition( ccp(size.width/2, size.height/2) ); this->addChild(pSprite, 0); return true; } void HelloWorld::registerWithTouchDispatcher() { CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true); } void HelloWorld::onEnter() { CCLayer::onEnter(); //這個父類的調(diào)用很重要! m_nCharLimit = 12; m_pTextFieldAction = CCRepeatForever::create( CCSequence::create( CCFadeOut::create(0.25), CCFadeIn::create(0.25), NULL )); m_pTextFieldAction->retain(); //這里一定要retain一次,否則會出現(xiàn)內(nèi)存問題。 m_bAction = false; // add CCTextFieldTTF CCSize s = CCDirector::sharedDirector()->getWinSize(); m_pTextField = CCTextFieldTTF::textFieldWithPlaceHolder("<click here for input>", FONT_NAME, FONT_SIZE); m_pTextField->setColor(ccWHITE); //設(shè)置輸入編輯框中字符的顏色 // m_pTextField->setSecureTextEntry(true); //輸入密碼時,用點字符替代 m_pTextField->setDelegate(this); m_pTextField->setPosition(ccp(s.width / 2, s.height / 2-30)); //將輸入編輯框的y軸位置設(shè)低是為了測試,當(dāng)出現(xiàn)鍵盤的時候,輸入編輯框的自動向上調(diào)整。 addChild(m_pTextField); } //返回節(jié)點的rect static CCRect getRect(CCNode * pNode) { CCRect rc; rc.origin = pNode->getPosition(); rc.size = pNode->getContentSize(); rc.origin.x -= rc.size.width / 2; rc.origin.y -= rc.size.height / 2; return rc; } bool HelloWorld::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent) { CCLOG("++++++++++++++++++++++++++++++++++++++++++++"); m_beginPos = pTouch->getLocation(); return true; } void HelloWorld::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent) { if (! m_pTextField) { return; } CCPoint endPos = pTouch->getLocation(); // 以下這部分代碼是用于檢測 begin touch 到 end touch之間的距離是否超過5.0,如果是,則返回;否則,繼續(xù)執(zhí)行下面的判斷是否點擊到編輯框的代碼。 float delta = 5.0f; if (::abs(endPos.x - m_beginPos.x) > delta || ::abs(endPos.y - m_beginPos.y) > delta) { // not click m_beginPos.x = m_beginPos.y = -1; return; } // decide the trackNode is clicked. CCRect rect; rect = getRect(m_pTextField); this->onClickTrackNode(rect.containsPoint(endPos)); CCLOG("----------------------------------"); } void HelloWorld::onClickTrackNode(bool bClicked) { if (bClicked) { // TextFieldTTFTest be clicked CCLOG("attachWithIME"); m_pTextField->attachWithIME(); //調(diào)用鍵盤 } else { // TextFieldTTFTest not be clicked CCLOG("detachWithIME"); m_pTextField->detachWithIME(); //隱藏鍵盤 } } void HelloWorld::onExit() { m_pTextFieldAction->release(); CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this); } // CCTextFieldDelegate protocol bool HelloWorld::onTextFieldAttachWithIME(CCTextFieldTTF * pSender) { if (! m_bAction) { m_pTextField->runAction(m_pTextFieldAction); m_bAction = true; } return false; } bool HelloWorld::onTextFieldDetachWithIME(CCTextFieldTTF * pSender) { if (m_bAction) { m_pTextField->stopAction(m_pTextFieldAction); m_pTextField->setOpacity(255); m_bAction = false; } return false; } bool HelloWorld::onTextFieldInsertText(CCTextFieldTTF * pSender, const char * text, int nLen) { // if insert enter, treat as default to detach with ime if ('\n' == *text) { return false; } // if the textfield's char count more than m_nCharLimit, doesn't insert text anymore. if (pSender->getCharCount() >= m_nCharLimit) { return true; } // create a insert text sprite and do some action CCLabelTTF * label = CCLabelTTF::create(text, FONT_NAME, FONT_SIZE); this->addChild(label); ccColor3B color = { 226, 121, 7}; label->setColor(color); // move the sprite from top to position CCPoint endPos = pSender->getPosition(); if (pSender->getCharCount()) { endPos.x += pSender->getContentSize().width / 2; } CCSize inputTextSize = label->getContentSize(); CCPoint beginPos(endPos.x, CCDirector::sharedDirector()->getWinSize().height - inputTextSize.height * 2); float duration = 0.5; label->setPosition(beginPos); label->setScale(8); CCAction * seq = CCSequence::create( CCSpawn::create( CCMoveTo::create(duration, endPos), CCScaleTo::create(duration, 1), CCFadeOut::create(duration), 0), CCCallFuncN::create(this, callfuncN_selector(HelloWorld::callbackRemoveNodeWhenDidAction)), 0); label->runAction(seq); return false; } bool HelloWorld::onTextFieldDeleteBackward(CCTextFieldTTF * pSender, const char * delText, int nLen) { // create a delete text sprite and do some action CCLabelTTF * label = CCLabelTTF::create(delText, FONT_NAME, FONT_SIZE); this->addChild(label); // move the sprite to fly out CCPoint beginPos = pSender->getPosition(); CCSize textfieldSize = pSender->getContentSize(); CCSize labelSize = label->getContentSize(); beginPos.x += (textfieldSize.width - labelSize.width) / 2.0f; CCSize winSize = CCDirector::sharedDirector()->getWinSize(); CCPoint endPos(- winSize.width / 4.0f, winSize.height * (0.5 + (float)rand() / (2.0f * RAND_MAX))); float duration = 1; float rotateDuration = 0.2f; int repeatTime = 5; label->setPosition(beginPos); CCAction * seq = CCSequence::create( CCSpawn::create( CCMoveTo::create(duration, endPos), CCRepeat::create( CCRotateBy::create(rotateDuration, (rand()%2) ? 360 : -360), repeatTime), CCFadeOut::create(duration), 0), CCCallFuncN::create(this, callfuncN_selector(HelloWorld::callbackRemoveNodeWhenDidAction)), 0); label->runAction(seq); return false; } bool HelloWorld::onDraw(CCTextFieldTTF * pSender) { return false; } void HelloWorld::callbackRemoveNodeWhenDidAction(CCNode * pNode) { this->removeChild(pNode, true); } void HelloWorld::keyboardWillShow(CCIMEKeyboardNotificationInfo& info) { CCLOG("TextInputTest:keyboardWillShowAt(origin:%f,%f, size:%f,%f)", info.end.origin.x, info.end.origin.y, info.end.size.width, info.end.size.height); if (! m_pTextField) { return; } CCRect rectTracked = getRect(m_pTextField); CCLOG("TextInputTest:trackingNodeAt(origin:%f,%f, size:%f,%f)", rectTracked.origin.x, rectTracked.origin.y, rectTracked.size.width, rectTracked.size.height); // if the keyboard area doesn't intersect with the tracking node area, nothing need to do. if (! rectTracked.intersectsRect(info.end)) { return; } // assume keyboard at the bottom of screen, calculate the vertical adjustment. //計算出需要y軸需要調(diào)整的距離 adjustVert = info.end.getMaxY() - rectTracked.getMinY(); CCLOG("TextInputTest:needAdjustVerticalPosition(%f)", adjustVert); // move all the children node of KeyboardNotificationLayer CCArray * children = getChildren(); CCNode * node = 0; int count = children->count(); CCPoint pos; for (int i = 0; i < count; ++i) { node = (CCNode*)children->objectAtIndex(i); pos = node->getPosition(); pos.y += adjustVert; //所有的節(jié)點都向上移動 node->setPosition(pos); } } void HelloWorld::keyboardWillHide(CCIMEKeyboardNotificationInfo &info) { CCLOG("TextInputTest:keyboardWillShowAt(origin:%f,%f, size:%f,%f)", info.end.origin.x, info.end.origin.y, info.end.size.width, info.end.size.height); CCArray * children = getChildren(); CCNode * node = 0; int count = children->count(); CCPoint pos; for (int i = 0; i < count; ++i) { node = (CCNode*)children->objectAtIndex(i); pos = node->getPosition(); pos.y -= adjustVert; //所有的節(jié)點都向下移動,恢復(fù)原來的位置 node->setPosition(pos); } } 大概就是這么多內(nèi)容了吧!了解了一下CCEditBox & CCTextFieldTTF,感覺前者使用過程比較簡單,后者比較復(fù)雜一些,但是可以增加一些特殊效果。
例外附上一個 使用 CCTextFieldTTF 創(chuàng)建的 帶光標(biāo)的輸入框 文章! |
|