cocos2d-x带光标的输入框的实现,
头文件代码如下:
#ifndef __CC_CURSOR_TEXTFIELD_H__
#define __CC_CURSOR_TEXTFIELD_H__
#include "cocos2d.h"
#include "cocos-ext.h"
USING_NS_CC;
USING_NS_CC_EXT;
//
/// CCCursorTextField - 带光标文本输入框
//
class CCCursorTextField :public CCTextFieldTTF, public CCTouchDelegate, public CCTextFieldDelegate
{
public:
enum TextLineMode
{
TLM_ONELINE, //单行
TLM_AUTOLINE, //自动换行
};
public:
CCCursorTextField ();
virtual ~CCCursorTextField ();
static CCCursorTextField* textFieldWithPlaceHolder(const char* placeHolder, CCSize contentSize, const char* cursorImg);
private:
// 初始化光标精灵
void initCursorSprite(const char* scale9Img);
public:
void onEnter();
void onExit();
//设置结束回调
void setEndCallBack(CCObject* target, SEL_CallFuncN func);
// 得到TextField矩形
CCRect getTextFieldRect();
// 判断是否点击在TextField处
bool isInTextField(CCTouch* pTouch);
//设置是否以星号显示字符 (密码框)
void setSecureTextEntry(bool value);
//设置输入法位移高度(输入法出来后需要偏移)
void setInputOffsetH(int h) { _InputOffsetH = h; }
//设置文本换行模式
void setTextLineMode(TextLineMode mode) { _LineMode = mode; }
//设置、获取文本
const char* getText(){ return _InputText.c_str() ;}
void setText(const char* pText);
//最大字符数控制
void setMaxBytes(uint32_t bytes) { _MaxBytes = bytes ;}
void setMaxChars(uint32_t chars) { _MaxNChars = chars; }
// 打开/关闭输入法
void openIME();
void closeIME();
//设置触摸层级
void setPriority(int Priority);
//设置字体颜色和光标颜色
void setFontOptions(const char* fontName, int fontSize=20, ccColor3B color=ccc3(255,255,255));
//设置输入框的宽度
void setTextFieldWidth(uint32_t width);
//接口实现
protected:
// 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
virtual bool ccTouchBegan(CCTouch* pTouch, CCEvent* pEvent);
virtual void ccTouchEnded(CCTouch* pTouch, CCEvent* pEvent);
//CCIMEDelegate
virtual void insertText(const char * text, int len);
private:
//获取指定字符串的尺寸信息
CCSize getTextDimension(const char* pText);
//判断字符是否超出长度 按字符删除 并返回是否超出规定字符数
bool removeChars(std::string* pText, int maxLength, bool isNewline);
//判断字符是否超出长度 按字符删除 并返回是否超出规定字节数
bool removeBytes(std::string* pText, int maxBytes, bool isNewline);
private:
int _InputOffsetH; //输入法高度偏移
CCObject* _EndTarget; //结束回调目标
SEL_CallFuncN _EndFunc; //结束回调函数
CCEditBoxImpl* _EditBoxImpl; //触摸本
bool _IsTouchEnabled; //是否开启触摸
CCPoint _ClickPos; //点击开始位置
CCScale9Sprite* _CursorSprite; //光标精灵
CCAction* _CursorAction; //光标动画
CCPoint _CursorPos; //光标坐标
CCPoint _CursorLastPos; //光标失去焦点后的最后坐标
std::string _InputText; //输入框文本内容
uint32_t _MaxBytes; //最大字节数
uint32_t _InputBytes; //输入字节数
uint32_t _MaxNChars; //最大字符数(中文、英文都算一个字符)
float _ContentHeight; //内容高度
float _ContentWidth; //输入框宽度
TextLineMode _LineMode; //换行模式
};
#endif //__CC_AUZ_CODE_LAYER_H__
cpp实现文件代码如下:
#include "CCCursorTextField.h"
#include "CCGL.h"
//
//计算行距(有待改进)
#define kROWSPACING(FONTSIZE) (2.5f + (FONTSIZE/10-2) * 0.5f + (FONTSIZE-20) * 0.2f + 0.3f)
//字距(密码框)
#define kFONTSPACING(FONTSIZE) (int)((int)FONTSIZE%3 != 0 ? FONTSIZE/3 + 1 : FONTSIZE/3 + (FONTSIZE >= 20 ? 1 : 0))
CCCursorTextField::CCCursorTextField(): _CursorSprite(NULL), _CursorAction(NULL), _InputText(""),
_MaxBytes(0), _MaxNChars(0), _InputBytes(0), _LineMode(TLM_ONELINE)
{
_EndTarget = NULL;
_EndFunc = NULL;
_IsTouchEnabled = false;
_CursorPos = ccp(0 ,0);
_CursorLastPos = ccp(0, 0);
}
CCCursorTextField::~CCCursorTextField()
{
}
void CCCursorTextField::initCursorSprite(const char* scale9Img)
{
_CursorSprite = CCScale9Sprite::create(scale9Img);
_CursorSprite->setPreferredSize(CCSizeMake(2.0f, this->getFontSize()));
CCSize winSize = getContentSize();
_CursorSprite->setAnchorPoint(ccp(0,1));
_CursorPos = _CursorLastPos.x != 0 && _CursorLastPos.y != 0 ? _CursorLastPos : ccp(2, winSize.height - kROWSPACING(getFontSize()));
_CursorSprite->setPosition(_CursorPos);
this->addChild(_CursorSprite);
_CursorAction = CCRepeatForever::create((CCActionInterval*) CCSequence::create(CCFadeOut::create(0.5f), CCFadeIn::create(0.5f), NULL));
_CursorSprite->runAction(_CursorAction);
_CursorSprite->setVisible(false);
this->setDelegate(this);
}
CCCursorTextField * CCCursorTextField::textFieldWithPlaceHolder(const char* placeHolder, CCSize contentSize, const char* cursorImg)
{
CCCursorTextField* pTextField = new CCCursorTextField();
if(pTextField && pTextField->initWithString("", "", 20))
{
pTextField->setDimensions(contentSize);
pTextField->_ContentHeight = contentSize.height;
pTextField->_ContentWidth = contentSize.width;
pTextField->autorelease();
if (placeHolder)
pTextField->setPlaceHolder(placeHolder);
pTextField->initCursorSprite(cursorImg);
return pTextField;
}
CC_SAFE_DELETE(pTextField);
return NULL;
}
void CCCursorTextField::setEndCallBack(CCObject* target,SEL_CallFuncN func)
{
this->_EndTarget = target;
this->_EndFunc = func;
}
CCSize CCCursorTextField::getTextDimension(const char* text)
{
return CCLabelTTF::create(text, this->getFontName(), this->getFontSize())->getContentSize();
}
CCRect CCCursorTextField::getTextFieldRect()
{
CCRect aRect;
aRect.size = getContentSize();
aRect.origin.x = 0;
aRect.origin.y = 0;
return aRect;
}
void CCCursorTextField::setText(const char* pText)
{
_InputText.clear();
std::string pTempText(pText);
_CursorPos.y = _ContentHeight - kROWSPACING(getFontSize());
//判断字符是否超出最大字符数
this->removeChars(&pTempText, _MaxNChars, false);
//判断字符是否超出最大字节数
this->removeBytes(&pTempText, _MaxBytes, false);
//算出当前共有几个字符
std::string tempStr(pTempText);
std::vector<std::string> vecChars;
std::string iTem;
//计算字符串的字符数 并添加到 Vector中
while(tempStr.size() > 0)
{
std::string iStr;
int delLen = 1;
while(0x80 == (0xC0 & tempStr.at(tempStr.size() - delLen))) { delLen++; }
while(delLen > 0) { iStr.insert(iStr.begin(), tempStr[tempStr.size()-1]); tempStr.erase(tempStr.end()-1); delLen--; }
vecChars.insert(vecChars.begin(), iStr);
}
//处理要显示在输入框中的值
for (size_t i = 0; i < vecChars.size(); i ++)
{
iTem.append(vecChars[i]);
//判断是否换行
if (getTextDimension(iTem.c_str()).width > _ContentWidth - 5)
{
_CursorPos.x = getTextDimension(iTem.c_str()).width + 2;
//如果是单行模式则直接结束方法
if (_LineMode == TLM_ONELINE)
{
_CursorPos.x -= getTextDimension(vecChars[i].c_str()).width;
_InputBytes = _InputText.size();
setString(_InputText.c_str());
_CursorLastPos = _CursorPos;
_CursorSprite->setPosition(_CursorPos);
return;
}
_CursorPos.y -= getFontSize() + kROWSPACING(getFontSize());
//添加换行
_InputText.append("\n");
//清除当前行 添加新行的数据
iTem.clear();
iTem.append(vecChars[i]);
}
else//计算光标的 x轴
{
//判断是否是密码框 密码+kFONTSPACING(getFontSize()) 普通+getTextDimension(iTem.c_str()).width + 2
_CursorPos.x = isSecureTextEntry() ? (iTem.size()*kFONTSPACING(getFontSize())) : getTextDimension(iTem.c_str()).width + 2;
}
_InputText.append(vecChars[i]);
}
_InputBytes = _InputText.size();
setString(_InputText.c_str());
_CursorLastPos = _CursorPos;
_CursorSprite->setPosition(_CursorPos);
}
bool CCCursorTextField::isInTextField(CCTouch* pTouch)
{
CCRect aRect = getTextFieldRect();
CCPoint aScreenPoint = this->convertToNodeSpace(pTouch->getLocation());
return aRect.containsPoint(aScreenPoint);
}
bool CCCursorTextField::ccTouchBegan(CCTouch* pTouch, CCEvent* pEvent)
{
_ClickPos = pTouch->getLocationInView();
_ClickPos = CCDirector::sharedDirector()->convertToGL(_ClickPos);
return true;
}
void CCCursorTextField::setSecureTextEntry(bool value)
{
//当不是多行模式的时候 才能设置字符以星号显示
if (_LineMode != TLM_AUTOLINE)
{
CCTextFieldTTF::setSecureTextEntry(value);
}
}
void CCCursorTextField::ccTouchEnded(CCTouch* pTouch, CCEvent* pEvent)
{
//是否打开输入法
isInTextField(pTouch) ? openIME() : closeIME();
}
bool CCCursorTextField::onTextFieldInsertText(CCTextFieldTTF* pSender, const char* text, int nLen)
{
if(_CursorPos.y > getFontSize())
{
//设置文本内容
_InputText.append(text);
setString(_InputText.c_str());
}
//判断是否是密码框
if (isSecureTextEntry())
{
_CursorPos.x += kFONTSPACING(getFontSize());
_CursorLastPos = _CursorPos;
_CursorSprite->setPosition(_CursorPos);
return true;
}
if(0 != strcmp(text, "\n") && _CursorPos.y > getFontSize() && _CursorPos.x < _ContentWidth - 5)
{
_CursorPos.x += getTextDimension(text).width;
}
if(_LineMode == TLM_ONELINE)
{
if(_CursorPos.x >= getTextDimension(text).width + 2)
{
_CursorSprite->setPosition(_CursorPos);
}
else
{
_InputText = _InputText.substr(0, _InputBytes - nLen);
_InputBytes = _InputText.size();
this->setString(_InputText.c_str());
}
}
else
{
_CursorSprite->setPosition(_CursorPos);
}
//保存光标的最后位置
_CursorLastPos = _CursorPos;
return true;
}
bool CCCursorTextField::onTextFieldDeleteBackward(CCTextFieldTTF* pSender, const char* delText, int nLen)
{
//判断文本框中是否还有字符
if (strlen(_InputText.c_str()) - strlen(delText) < 1)
{
_InputBytes = 0;
_InputText = "";
_CursorPos.x = 2;
_CursorLastPos = _CursorPos;
_CursorSprite->setPosition(_CursorPos);
return false;
}
//删除字符
std::string tempText = delText;
_InputText.resize(_InputText.size() - nLen);
setString(_InputText.c_str());
_InputBytes -= nLen ;
//判断是是否是密码框
if (isSecureTextEntry())
{
_CursorPos.x -= kFONTSPACING(getFontSize());
_CursorLastPos = _CursorPos;
_CursorSprite->setPosition(_CursorPos);
return false;
}
//设置光标位置
if(_CursorPos.x > 2)
{
_CursorPos.x -= getTextDimension(tempText.c_str()).width;
}
else if(_LineMode != TLM_ONELINE)
{
uint32_t nFoundPos = _InputText.rfind("\n");
_CursorPos.y += getFontSize() + kROWSPACING(getFontSize());
if(nFoundPos != std::string::npos)
{
std::string sTmp(_InputText, nFoundPos + 1);
_CursorPos.x = getTextDimension(sTmp.c_str()).width;
}
else if(0 != _InputText.length())
{
_CursorPos.x = getTextDimension(_InputText.c_str()).width;
}
else
{
_CursorPos.x = 0;
}
}
_CursorLastPos = _CursorPos;
_CursorSprite->setPosition(_CursorPos);
return false;
}
void CCCursorTextField::setPriority(int Priority)
{
if(this->_IsTouchEnabled)
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
this->_IsTouchEnabled = true;
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, Priority, false);
}
void CCCursorTextField::insertText(const char* text, int len)
{
this->setPlaceHolder("");
//如果是回车键
if(strcmp(text,"\n") == 0)
{
detachWithIME();
return;
}
std::string sTmp;
_InputBytes += len;
sTmp.assign(_InputText.begin(), _InputText.end());
sTmp.append(text);
//文本内容长度超出控件长度,自动换行
float nTextWidth = getTextDimension(sTmp.c_str()).width;
bool bAutoLineBreak = nTextWidth > _ContentWidth - 5;
//如果判断最大字符数
if(_MaxNChars > 0)
{
//算出当前共有几个字符
std::string tempStr = _InputText;
tempStr.append(text);
//超过字符则缩减 如果在 换行时 字符串加上换行符 大于 最大字符数的话也要进行缩减
if(removeChars(&tempStr, _MaxNChars, bAutoLineBreak))
{
_InputText = tempStr;
this->setString(tempStr.c_str());
_InputBytes = tempStr.size();
//detachWithIME();
return;
}
}
//如果最大字节数不为0,则处理最大字节数
if(_MaxBytes > 0 && _InputBytes >= _MaxBytes)
{
_InputText.append(text);
std::string tempStr = _InputText;
//往前删除一个Unicode字符直到m_pInputText的长度满足要求
removeBytes(&tempStr, _MaxBytes, bAutoLineBreak);
_InputText = tempStr;
this->setString(tempStr.c_str());
_InputBytes = tempStr.size();
//detachWithIME();
return ;
}
//如果是单行模式 判断当前行是否超出最大宽度
if (_LineMode == TLM_ONELINE && _CursorPos.x >= _ContentWidth - getTextDimension(text).width - 5)
{
this->setString(_InputText.c_str());
return;
}
char* strReturn = "\n";
//判断文本长度是否超过输入框长度
char* pChar = (nTextWidth > _ContentWidth - 5 && _LineMode != TLM_ONELINE) ? strReturn : const_cast<char*>(text);
std::string sInsert(pChar, len);
int nPos = sInsert.find('\n');
if((int)sInsert.npos != nPos)
{
len = nPos;
sInsert.erase(nPos);
}
//输入文本内容
if(len > 0)
{
//激活委托事件
if(m_pDelegate && m_pDelegate->onTextFieldInsertText(this, sInsert.c_str(), len))
{
return;
}
std::string sText(_InputText);
sText.append(sInsert);
setString(sText.c_str());
}
if((int)sInsert.npos == nPos)
return;
//文本换行
if(_LineMode != TLM_ONELINE && m_pDelegate && m_pDelegate->onTextFieldInsertText(this, "\n", 1))
{
//换行后,插入剩下的文本内容
if(bAutoLineBreak)
{
_InputBytes += 1;
//换行后,设置光标的X坐标位置
_CursorPos.x = getTextDimension(text).width + 2;
//换行后,文本内容高度-_FontSize (根据字体大小调整行距)
_CursorPos.y -= getFontSize() + kROWSPACING(getFontSize());
_InputText.append(text);
setString(_InputText.c_str());
m_pDelegate->onTextFieldInsertText(this, sInsert.c_str(), len);
}
return;
}
detachWithIME();
}
bool CCCursorTextField::removeChars(std::string* pText, int maxLength, bool isNewline)
{
//是否超出规定字符数
bool isOutRange = false;
//算出当前共有几个字符
std::vector<std::string> vecChars;
while((*pText).size() > 0)
{
std::string iStr;
int delLen = 1;
while(0x80 == (0xC0 & (*pText).at((*pText).size() - delLen))) { delLen++; }
while(delLen > 0) { iStr.insert(iStr.begin(), (*pText)[(*pText).size()-1]); (*pText).erase((*pText).end()-1); delLen--; }
vecChars.insert(vecChars.begin(), iStr);
}
//超过字符则缩减 如果在 换行时 字符串加上换行符 大于 最大字符数的话也要进行缩减
if(vecChars.size() > maxLength - (isNewline ? 1 : 0) - 1)
{
while(vecChars.size() > maxLength - 1)
vecChars.erase(vecChars.end() - 1);
isOutRange = true;
}
(*pText).clear();
for (size_t i=0; i<vecChars.size(); i++)
(*pText).append(vecChars[i]);
return isOutRange;
}
bool CCCursorTextField::removeBytes(std::string* pText, int maxBytes, bool isNewline)
{
//是否超出规定字节数
bool isOutRange = false;
//往前删除一个Unicode字符直到m_pInputText的长度满足要求
while((*pText).size() > 0 && (*pText).size() > _MaxBytes - (isNewline ? 1 : 0) - 1)
{
int delLen = 1;
while(0x80 == (0xC0 & (*pText).at((*pText).size() - delLen))) { delLen++; }
while(delLen > 0) { (*pText).erase((*pText).end()-1); delLen--; }
isOutRange = true;
}
return isOutRange;
}
void CCCursorTextField::setFontOptions(const char* fontName, int fontSize, ccColor3B color)
{
//设置文本
this->setColor(color);
this->setFontName(fontName);
this->setFontSize(fontSize);
this->_CursorSprite->setColor(color);
this->_CursorSprite->setPreferredSize(CCSizeMake(2.0f, this->getFontSize()));
}
bool CCCursorTextField::onTextFieldAttachWithIME(CCTextFieldTTF* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
if(this->getPositionY() < this->_InputOffsetH)
{
GAME_SCENE->stopAllActions();
CCMoveTo* moveTo = CCMoveTo::create(0.2f, ccp(0, this->_InputOffsetH - this->getPositionY()));
GAME_SCENE->runAction(CCSequence::create(CCDelayTime::create(0.2f),moveTo,NULL));
}
else
{
CCMoveTo* moveTo = CCMoveTo::create(0.2f, ccp(0,0));
GAME_SCENE->runAction(CCSequence::create(CCDelayTime::create(0.2f),moveTo,NULL));
}
#endif
_CursorSprite->setVisible(true);
return false;
}
bool CCCursorTextField::onTextFieldDetachWithIME(CCTextFieldTTF* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
if(GAME_SCENE->getPositionY() != 0)
{
GAME_SCENE->stopAllActions();
CCMoveTo* moveTo = CCMoveTo::create(0.2f, ccp(0,0));
GAME_SCENE->runAction(moveTo);
}
if(this->_EndTarget && this->_EndFunc)
(_EndTarget->*_EndFunc)(this);
#endif
_CursorSprite->setVisible(false);
return false;
}
void CCCursorTextField::openIME()
{
attachWithIME();
}
void CCCursorTextField::closeIME()
{
detachWithIME();
}
void CCCursorTextField::onEnter()
{
CCTextFieldTTF::onEnter();
if(!_IsTouchEnabled)
{
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, kCCMenuHandlerPriority, false);
_IsTouchEnabled = true;
}
}
void CCCursorTextField::onExit()
{
CCTextFieldTTF::onExit();
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
//还原GameScene
if(this->getPositionY() > 0)
{
//GAME_SCENE->stopAllActions();
CCMoveTo* moveTo = CCMoveTo::create(0.2f, ccp(0,0));
//GAME_SCENE->runAction(CCSequence::create(CCDelayTime::create(0.2f), moveTo, NULL));
}
}
搞完啦输入框,但是还有点小问题,不过对付日常使用足够啦,准备弄一下用freetype实现的富文本。有经验的可以一起交流下啊。
本人qq:1587257962