cocos2d-x带光标的输入框实现

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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值