转自:http://blog.csdn.net/we000636/article/details/8829172
聊天输入框 (单行输入框 ,多行可自己扩展)
实现功能:
1.普通输入
2.设置输入框显示最大宽度(PT值,cocos2d-x坐标值)
3.设置输入框允许的最大字符数量(字符Unicode)
4.输入框自动缩进(当输入字符串数量超过显示框最大宽度时,会自动向左缩进,显示最新字符串
输入框实现代码
头文件:
- #ifndefCursorInputDemo_CursorTextField_h
- #defineCursorInputDemo_CursorTextField_h
- #include"cocos2d.h"
- USING_NS_CC;
- classCursorTextField:publicCCTextFieldTTF,publicCCTextFieldDelegate,publicCCTouchDelegate
- {
- private:
- //点击开始位置
- CCPointm_beginPos;
- //光标精灵
- CCSprite*m_pCursorSprite;
- //光标动画
- CCAction*m_pCursorAction;
- //光标坐标
- CCPointm_cursorPos;
- //输入框长度
- floatinputFrameWidth;
- //允许输入的最大字符数Unicode
- floatinputMaxLength;
- intnLenCount;
- int*codeNumType;//每个字符对应的字节数量
- intcodeCur;//当前第几个字符
- intstartCur;//行开头字符下标
- intendCur;//行末尾下标
- //输入框总内容
- std::string*m_pInputText;
- std::stringinpuText;//当前输入框内容
- public:
- CursorTextField();
- ~CursorTextField();
- //static
- staticCursorTextField*textFieldWithPlaceHolder(constchar*placeholder,constchar*fontName,floatfontSize);
- //CCLayer
- voidonEnter();
- voidonExit();
- boolinit();
- //初始化光标精灵
- voidinitCursorSprite(intnHeight);
- //CCTextFieldDelegate
- virtualboolonTextFieldAttachWithIME(CCTextFieldTTF*pSender);
- virtualboolonTextFieldDetachWithIME(CCTextFieldTTF*pSender);
- virtualboolonTextFieldInsertText(CCTextFieldTTF*pSender,constchar*text,intnLen);
- virtualboolonTextFieldDeleteBackward(CCTextFieldTTF*pSender,constchar*delText,intnLen);
- //CCLayerTouch
- boolccTouchBegan(CCTouch*pTouch,CCEvent*pEvent);
- voidccTouchEnded(CCTouch*pTouch,CCEvent*pEvent);
- //判断是否点击在TextField处
- boolisInTextField(CCTouch*pTouch);
- //得到TextField矩形
- CCRectgetRect();
- //打开输入法
- voidopenIME();
- //关闭输入法
- voidcloseIME();
- constchar*getInputText();
- voidsetInpuntText(char*text);
- voidsetInputWidth(floatwidth);
- voidsetInputMaxLength(floatlength);
- intUtf82Unicode(LPWSTRout,intoutsize,LPSTRin,intinsize);
- };
- #endif
cpp文件:
- #include"CursorTextField.h"
- conststaticfloatDELTA=0.5f;
- usingnamespacecocos2d;
- usingnamespacestd;
- 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);
- }
- voidCursorTextField::onEnter()
- {
- CCTextFieldTTF::onEnter();
- CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,false);
- this->setDelegate(this);
- }
- CursorTextField*CursorTextField::textFieldWithPlaceHolder(constchar*placeholder,constchar*fontName,floatfontSize)
- {
- CursorTextField*pRet=newCursorTextField();
- if(pRet&&pRet->initWithString("",fontName,fontSize))
- {
- pRet->autorelease();
- if(placeholder)
- {
- pRet->setPlaceHolder(placeholder);
- }
- pRet->init();
- pRet->initCursorSprite(fontSize);
- pRet->setHorizontalAlignment(kCCTextAlignmentLeft);
- returnpRet;
- }
- CC_SAFE_DELETE(pRet);
- returnNULL;
- }
- boolCursorTextField::init(){
- this->inputFrameWidth=400;
- this->inputMaxLength=38;
- this->nLenCount=0;
- this->codeNumType=newint[50];
- this->codeCur=0;
- this->startCur=0;
- this->endCur=0;
- inpuText="";
- returntrue;
- }
- voidCursorTextField::initCursorSprite(constintmHeight)
- {
- //初始化光标
- constintcolumn=4;
- constintnHeight=(constint)mHeight;
- intpixels[25][column];
- for(inti=0;i<nHeight;++i){
- for(intj=0;j<column;++j){
- pixels[i][j]=0xffffffff;
- }
- }
- CCTexture2D*texture=newCCTexture2D();
- texture->initWithData(pixels,kCCTexture2DPixelFormat_RGB888,1,1,CCSizeMake(column,nHeight));
- m_pCursorSprite=CCSprite::createWithTexture(texture);
- CCSizewinSize=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=newstd::string();
- }
- boolCursorTextField::ccTouchBegan(cocos2d::CCTouch*pTouch,cocos2d::CCEvent*pEvent)
- {
- m_beginPos=pTouch->getLocation();
- returntrue;
- }
- CCRectCursorTextField::getRect()
- {
- CCSizesize=getContentSize();
- returnCCRectMake(0,-size.height/2,inputFrameWidth,size.height);
- }
- //获取输入框内容
- constchar*CursorTextField::getInputText(){
- constchar*text=m_pInputText->c_str();
- returntext;
- }
- //设置输入框内容
- voidCursorTextField::setInpuntText(char*text){
- *m_pInputText="";
- setString(text);
- m_pCursorSprite->setPositionX(0);
- CC_SAFE_DELETE_ARRAY(codeNumType);
- codeNumType=newint[50];
- codeCur=0;
- startCur=0;
- endCur=0;
- inpuText="";
- }
- //设置输入框宽度一旦字符串宽度超度这个长度字符串会自动向左缩进
- voidCursorTextField::setInputWidth(floatwidth){
- this->inputFrameWidth=width;
- }
- //设置输入宽显示的最大字符数量Unicode
- voidCursorTextField::setInputMaxLength(floatlength){
- this->inputMaxLength=length;
- }
- //判断点击事件,是否响应在输入框范围内
- boolCursorTextField::isInTextField(cocos2d::CCTouch*pTouch)
- {
- returngetRect().containsPoint(convertTouchToNodeSpaceAR(pTouch));
- }
- voidCursorTextField::ccTouchEnded(cocos2d::CCTouch*pTouch,cocos2d::CCEvent*pEvent)
- {
- CCPointendPos=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();
- }
- //弹出手机键盘时响应事件
- boolCursorTextField::onTextFieldAttachWithIME(cocos2d::CCTextFieldTTF*pSender)
- {
- if(m_pInputText->empty()){
- returnfalse;
- }
- m_pCursorSprite->setPositionX(getContentSize().width);
- returnfalse;
- }
- //当有输入进来时响应
- //@parampSender发送事件对象
- //@paramtext输入内容
- //@param内容字节长度
- boolCursorTextField::onTextFieldInsertText(cocos2d::CCTextFieldTTF*pSender,constchar*text,intnLen)
- {
- std::stringsText=m_pInputText->c_str();
- wchar_t*wText=newwchar_t[200];
- chart[200];
- memset(t,0,200);
- strcpy(t,sText.c_str());
- //将字符串转换为Unicode,并返回Unicode字符数量
- intcou=Utf82Unicode(wText,200,t,sText.length());
- //当字符数量超过规定值不做处理
- if(cou>=inputMaxLength)returntrue;
- //屏蔽回车输入
- if(text[0]=='\n')
- returntrue;
- //输入框总内容添加
- m_pInputText->append(text);
- //测试
- CCLabelTTF*ttf=CCLabelTTF::create(text,"Verdana-Bold",26);
- floatteWidth=ttf->getContentSize().width;
- CCLOG("anycodelength---%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){
- intnnLen=nLen;
- if(codeNumType[startCur]==1){
- nnLen=1;
- }
- if(codeNumType[startCur]==3){
- nnLen=3;
- }
- startCur++;
- nLenCount+=nnLen;
- floatgap=localText->size()-nLenCount;
- inpuText=localText->substr(nLenCount,gap);
- setString(inpuText.c_str());
- floatcoWidth=getContentSize().width;
- }
- }
- else{
- //小于,直接设置显示总字符串
- nLenCount=0;
- startCur=0;
- setString(m_pInputText->c_str());
- }
- //设置光标位置
- m_pCursorSprite->setPositionX(getContentSize().width);
- CC_SAFE_DELETE_ARRAY(wText);
- returntrue;
- }
- //当有输入进来时响应
- //@parampSender发送事件对象
- //@paramtext删除内容
- //@param内容字节长度
- boolCursorTextField::onTextFieldDeleteBackward(cocos2d::CCTextFieldTTF*pSender,constchar*delText,intnLen)
- {
- //将总字符串长度减去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){
- intnnLen=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;
- floatgap=localText->size()-nLenCount;
- conststd::stringtext=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);
- }
- returntrue;
- }
- boolCursorTextField::onTextFieldDetachWithIME(cocos2d::CCTextFieldTTF*pSender)
- {
- returnfalse;
- }
- voidCursorTextField::openIME()
- {
- m_pCursorSprite->setVisible(true);
- this->attachWithIME();
- }
- voidCursorTextField::closeIME()
- {
- m_pCursorSprite->setVisible(false);
- this->detachWithIME();
- }
- voidCursorTextField::onExit()
- {
- CCTextFieldTTF::onExit();
- CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
- }
- intCursorTextField::Utf82Unicode(LPWSTRout,intoutsize,LPSTRin,intinsize)
- {
- //-------------------------------------------------------------------------------------------
- //参数有效性判断
- if(out==NULL||in==NULL||insize<0)
- {
- return-1;
- }
- inttypeCount=0;
- inttotalNum=0;
- char*p=in;
- for(inti=0;i<insize;i++)
- {
- if(*p>=0x00&&*p<=0x7f)//说明最高位为'0',这意味着utf8编码只有1个字节!
- {
- p++;
- totalNum+=1;
- }
- elseif((*p&(0xe0))==0xc0)//只保留最高三位,看最高三位是不是110,如果是则意味着utf8编码有2个字节!
- {
- p++;
- p++;
- totalNum+=1;
- }
- elseif((*p&(0xf0))==0xe0)//只保留最高四位,看最高三位是不是1110,如果是则意味着utf8编码有3个字节!
- {
- p++;
- p++;
- p++;
- totalNum+=1;
- }
- }
- if(outsize<totalNum)//参数有效性判断!
- {
- return-1;
- }
- //------------------------------------------------
- intresultsize=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;
- }
- elseif((*p&0xe0)==0xc0)//只保留最高三位,看最高三位是不是110,如果是则意味着utf8编码有2个字节!
- {
- wchar_tt=0;
- chart1=0;
- chart2=0;
- t1=*p&(0x1f);//高位的后5位!(去除了头部的110这个标志位)
- p++;
- t2=*p&(0x3f);//低位的后6位!(去除了头部的10这个标志位)
- *tmp=t2|((t1&(0x03))<<6);
- tmp++;
- *tmp=t1>>2;//留下其保留的三位
- tmp++;
- resultsize+=1;
- }
- elseif((*p&(0xf0))==0xe0)//只保留最高四位,看最高三位是不是1110,如果是则意味着utf8编码有3个字节!
- {
- wchar_tt=0;
- wchar_tt1=0;
- wchar_tt2=0;
- wchar_tt3=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;
- */
- returnresultsize;
- }
上面代码是通是UTF-8转Unicode来获取字符数量。当输入字符数量过多时,可能字节大小会超过声明的char数组大小,导致出现越界情况,程序崩溃。
解决方法一:根据限定字符数量,将char数组大小声明为最大数量,来避免越界情况出生
解决方法二:定义一个私有变量unicodeCount(名字随意取)来记录输入字符的总数量。由于onTextFieldInsertText,onTextFieldDeleteBackward这两个方法都是在我们输入一个完整字符或者减去一个完整字符时调用一次,所以将unicodeCount++放入onTextFieldInsertText,将unicodeCount--放入onTextFieldDeleteBackward中,可以完成输入字符数量的保存。这样就可以免去UTF-8转Unicode的操作,完全避免越界情况产生,也提高了效率
效率方面尚未优化,请参考自行优化,只提供一个解决思路
接下来将会写一篇光于cocos2d-普通文本显示框,不支持富文本,主要提供自动换行解决思路,以解决当前CCLabelTTF自动换行Bug的替代方案