轉自:http://bbs.9ria.com/thread-216948-1-10.htmlhtml
聊天輸入框 (單行輸入框 ,多行可本身擴展)數組
實現功能:app
1.普通輸入測試
2.設置輸入框顯示最大寬度(PT值,cocos2d-x座標值)優化
3.設置輸入框容許的最大字符數量(字符Unicode)動畫
4.輸入框自動縮進(當輸入字符串數量超過顯示框最大寬度時,會自動向左縮進,顯示最新字符串this
輸入框實現代碼編碼
頭文件:spa
#ifndef CursorInputDemo_CursorTextField_h #define CursorInputDemo_CursorTextField_h #include "cocos2d.h" USING_NS_CC; class CursorTextField: public CCTextFieldTTF, public CCTextFieldDelegate, public CCTouchDelegate { private: // 點擊開始位置 CCPoint m_beginPos; // 光標精靈 CCSprite *m_pCursorSprite; // 光標動畫 CCAction *m_pCursorAction; // 光標座標 CCPoint m_cursorPos; //輸入框長度 float inputFrameWidth; //容許輸入的最大字符數Unicode float inputMaxLength; int nLenCount; int* codeNumType; //每一個字符對應的字節數量 int codeCur; //當前第幾個字符 int startCur; //行開頭字符下標 int endCur; //行末尾下標 // 輸入框總內容 std::string *m_pInputText; std::string inpuText; //當前輸入框內容 public: CursorTextField(); ~CursorTextField(); // static static CursorTextField* textFieldWithPlaceHolder(const char *placeholder, const char *fontName, float fontSize); // CCLayer void onEnter(); void onExit(); bool init(); // 初始化光標精靈 void initCursorSprite(int nHeight); // 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); // CCLayer Touch bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent); void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent); // 判斷是否點擊在TextField處 bool isInTextField(CCTouch *pTouch); // 獲得TextField矩形 CCRect getRect(); // 打開輸入法 void openIME(); // 關閉輸入法 void closeIME(); const char* getInputText(); void setInpuntText(char* text); void setInputWidth(float width); void setInputMaxLength(float length); int Utf82Unicode(LPWSTR out, int outsize , LPSTR in,int insize); }; #endif
cpp文件:code
#include "CursorTextField.h" const static float DELTA = 0.5f; using namespace cocos2d; using namespace std; CursorTextField::CursorTextField() { CCTextFieldTTF(); m_pCursorSprite = NULL; m_pCursorAction = NULL; m_pInputText = NULL; codeNumType = NULL; } CursorTextField::~CursorTextField() { CC_SAFE_DELETE(m_pInputText); CC_SAFE_DELETE_ARRAY(codeNumType); } void CursorTextField::onEnter() { CCTextFieldTTF::onEnter(); CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,false); this->setDelegate(this); } CursorTextField * CursorTextField::textFieldWithPlaceHolder(const char *placeholder, const char *fontName, float fontSize) { CursorTextField *pRet = new CursorTextField(); if(pRet && pRet->initWithString("", fontName, fontSize)) { pRet->autorelease(); if (placeholder) { pRet->setPlaceHolder(placeholder); } pRet->init(); pRet->initCursorSprite(fontSize); pRet->setHorizontalAlignment(kCCTextAlignmentLeft); return pRet; } CC_SAFE_DELETE(pRet); return NULL; } bool CursorTextField::init(){ this->inputFrameWidth = 400; this->inputMaxLength = 38; this->nLenCount = 0; this->codeNumType = new int[50]; this->codeCur = 0; this->startCur = 0; this->endCur = 0; inpuText = ""; return true; } void CursorTextField::initCursorSprite(const int mHeight) { // 初始化光標 const int column = 4; const int nHeight = (const int)mHeight; int pixels[25][column]; for (int i=0; i<nHeight; ++i) { for (int j=0; j<column; ++j) { pixels[i][j] = 0xffffffff; } } CCTexture2D *texture = new CCTexture2D(); texture->initWithData(pixels, kCCTexture2DPixelFormat_RGB888, 1, 1, CCSizeMake(column, nHeight)); m_pCursorSprite = CCSprite::createWithTexture(texture); CCSize winSize = getContentSize(); m_cursorPos = ccp(0, winSize.height / 2); m_pCursorSprite->setPosition(m_cursorPos); this->addChild(m_pCursorSprite); m_pCursorSprite->setVisible(false); m_pCursorAction = CCRepeatForever::create((CCActionInterval *) CCSequence::create(CCFadeOut::create(0.25f), CCFadeIn::create(0.25f), NULL)); m_pCursorSprite->runAction(m_pCursorAction); m_pInputText = new std::string(); } bool CursorTextField::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent) { m_beginPos = pTouch->getLocation(); return true; } CCRect CursorTextField::getRect() { CCSize size = getContentSize(); return CCRectMake(0, -size.height / 2, inputFrameWidth, size.height); } //獲取輸入框內容 const char* CursorTextField::getInputText(){ const char* text = m_pInputText->c_str(); return text; } //設置輸入框內容 void CursorTextField::setInpuntText(char* text){ *m_pInputText = ""; setString(text); m_pCursorSprite->setPositionX(0); CC_SAFE_DELETE_ARRAY(codeNumType); codeNumType = new int[50]; codeCur = 0; startCur = 0; endCur = 0; inpuText = ""; } //設置輸入框寬度 一旦字符串寬度超度這個長度 字符串會自動向左縮進 void CursorTextField::setInputWidth(float width){ this->inputFrameWidth = width; } //設置輸入寬顯示的最大字符數量Unicode void CursorTextField::setInputMaxLength(float length){ this->inputMaxLength = length; } //判斷點擊事件,是否響應在輸入框範圍內 bool CursorTextField::isInTextField(cocos2d::CCTouch *pTouch) { return getRect().containsPoint(convertTouchToNodeSpaceAR(pTouch)); } void CursorTextField::ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent) { CCPoint endPos = pTouch->getLocation(); // 判斷是否爲點擊事件 if (::abs(endPos.x - m_beginPos.x) > DELTA || ::abs(endPos.y - m_beginPos.y)) { // 不是點擊事件 m_beginPos.x = m_beginPos.y = -1; return; } // 判斷是打開輸入法仍是關閉輸入法 isInTextField(pTouch) ? openIME() : closeIME(); } //彈出手機鍵盤時響應事件 bool CursorTextField::onTextFieldAttachWithIME(cocos2d::CCTextFieldTTF *pSender) { if (m_pInputText->empty()) { return false; } m_pCursorSprite->setPositionX(getContentSize().width); return false; } //當有輸入進來時響應 //@param pSender 發送事件對象 //@param text 輸入內容 //@param 內容字節長度 bool CursorTextField::onTextFieldInsertText(cocos2d::CCTextFieldTTF *pSender, const char *text, int nLen) { std::string sText = m_pInputText->c_str(); wchar_t* wText = new wchar_t[200]; char t[200]; memset(t,0,200); strcpy(t,sText.c_str()); //將字符串轉換爲Unicode,並返回Unicode字符數量 int cou = Utf82Unicode(wText,200,t,sText.length()); //當字符數量超過規定值 不作處理 if(cou >= inputMaxLength) return true; //屏蔽回車輸入 if(text[0] == '\n') return true; //輸入框總內容添加 m_pInputText->append(text); //測試 CCLabelTTF* ttf = CCLabelTTF::create(text,"Verdana-Bold",26); float teWidth = ttf->getContentSize().width; CCLOG("any code length---%f",teWidth); //輸入框當前字符串添加 inpuText.append(text); //當前字符的長度 codeNumType[codeCur++] = nLen; std::string* localText = m_pInputText; setString(m_pInputText->c_str()); //若是總字符串的長度大於指定寬度 if(getContentSize().width > inputFrameWidth){ //大於,截取字符串,直到字符串的長度小於指定寬度爲止 setString(inpuText.c_str()); while(getContentSize().width > inputFrameWidth){ int nnLen = nLen; if(codeNumType[startCur] == 1){ nnLen = 1; } if(codeNumType[startCur] == 3){ nnLen = 3; } startCur++; nLenCount += nnLen; float gap = localText->size() - nLenCount; inpuText = localText->substr(nLenCount,gap); setString(inpuText.c_str()); float coWidth = getContentSize().width; } } else{ //小於,直接設置顯示總字符串 nLenCount = 0; startCur = 0; setString(m_pInputText->c_str()); } //設置光標位置 m_pCursorSprite->setPositionX(getContentSize().width); CC_SAFE_DELETE_ARRAY(wText); return true; } //當有輸入進來時響應 //@param pSender 發送事件對象 //@param text 刪除內容 //@param 內容字節長度 bool CursorTextField::onTextFieldDeleteBackward(cocos2d::CCTextFieldTTF *pSender, const char *delText, int nLen) { //將總字符串長度減去nLen字節長 m_pInputText->resize(m_pInputText->size() - nLen); //當前字符數減一 codeNumType[codeCur--] = 0; std::string* localText = m_pInputText; setString(m_pInputText->c_str()); if(getContentSize().width > inputFrameWidth){ //大於指定寬度,截取字符串,直到字符串長度小於指定寬度 while(getContentSize().width > inputFrameWidth){ int nnLen = nLen; if(codeNumType[startCur - 1] == 1){ nnLen = 1; } if(codeNumType[startCur - 1] == 3){ nnLen = 3; } nLenCount -= nnLen; startCur--; if(startCur <=0) startCur = 0; if(nLenCount <=0 ) nLenCount = 0; float gap = localText->size() - nLenCount; const std::string text = localText->substr(nLenCount,gap); setString(text.c_str()); inpuText = text; } } else{ nLenCount = 0; startCur = 0; setString(m_pInputText->c_str()); } //設置光標位置 m_pCursorSprite->setPositionX(getContentSize().width); if (m_pInputText->empty()) { m_pCursorSprite->setPositionX(0); } return true; } bool CursorTextField::onTextFieldDetachWithIME(cocos2d::CCTextFieldTTF *pSender) { return false; } void CursorTextField::openIME() { m_pCursorSprite->setVisible(true); this->attachWithIME(); } void CursorTextField::closeIME() { m_pCursorSprite->setVisible(false); this->detachWithIME(); } void CursorTextField::onExit() { CCTextFieldTTF::onExit(); CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this); } int CursorTextField::Utf82Unicode(LPWSTR out, int outsize , LPSTR in,int insize) { //------------------------------------------------------------------------------------------- //參數有效性判斷 if(out == NULL || in == NULL || insize<0) { return -1; } int typeCount = 0; int totalNum = 0; char *p = in; for(int i=0;i<insize;i++) { if (*p >= 0x00 && *p <= 0x7f)//說明最高位爲'0',這意味着utf8編碼只有1個字節! { p++; totalNum += 1; } else if ((*p & (0xe0))== 0xc0)//只保留最高三位,看最高三位是否是110,若是是則意味着utf8編碼有2個字節! { p++; p++; totalNum += 1; } else if ((*p & (0xf0))== 0xe0)//只保留最高四位,看最高三位是否是1110,若是是則意味着utf8編碼有3個字節! { p++; p++; p++; totalNum += 1; } } if( outsize < totalNum )//參數有效性判斷! { return -1; } //------------------------------------------------ int resultsize = 0; p = in; char* tmp = (char *)out; while(*p) { if (*p >= 0x00 && *p <= 0x7f)//說明最高位爲'0',這意味着utf8編碼只有1個字節! { *tmp = *p; tmp++; //*tmp = '/0'; tmp++; resultsize += 1; } else if ((*p & 0xe0)== 0xc0)//只保留最高三位,看最高三位是否是110,若是是則意味着utf8編碼有2個字節! { wchar_t t = 0; char t1 = 0; char t2 = 0; t1 = *p & (0x1f);//高位的後5位!(去除了頭部的110這個標誌位) p++; t2 = *p & (0x3f);//低位的後6位!(去除了頭部的10這個標誌位) *tmp = t2 | ((t1 & (0x03)) << 6); tmp++; *tmp = t1 >> 2;//留下其保留的三位 tmp++; resultsize += 1; } else if ((*p & (0xf0))== 0xe0)//只保留最高四位,看最高三位是否是1110,若是是則意味着utf8編碼有3個字節! { wchar_t t = 0; wchar_t t1 = 0; wchar_t t2 = 0; wchar_t t3 = 0; t1 = *p & (0x1f); p++; t2 = *p & (0x3f); p++; t3 = *p & (0x3f); *tmp = ((t2 & (0x03)) << 6) | t3; tmp++; *tmp = (t1 << 4) | (t2 >> 2); tmp++; resultsize += 1; } p++; } /*不考慮結束符,若是考慮則打開此段! *tmp = '/0'; tmp++; *tmp = '/0'; resultsize += 2; */ return resultsize; }
上面代碼是通是UTF-8轉Unicode來獲取字符數量。當輸入字符數量過多時,可能字節大小會超過聲明的char數組大小,致使出現越界狀況,程序崩潰。
解決方法一:根據限定字符數量,將char數組大小聲明爲最大數量,來避免越界狀況出生
解決方法二:定義一個私有變量unicodeCount(名字隨意取)來記錄輸入字符的總數量。因爲onTextFieldInsertText,onTextFieldDeleteBackward這兩個方法都是在咱們輸入一個完整字符或者減去一個完整字符時調用一次,因此將unicodeCount++放入onTextFieldInsertText,將unicodeCount--放入onTextFieldDeleteBackward中,能夠完成輸入字符數量的保存。這樣就能夠免去UTF-8轉Unicode的操做,徹底避免越界狀況產生,也提升了效率
效率方面還沒有優化,請參考自行優化,只提供一個解決思路
接下來將會寫一篇光於cocos2d-普通文本顯示框,不支持富文本,主要提供自動換行解決思路,以解決當前CCLabelTTF自動換行Bug的替代方案