cocos2dx-3.10 聊天系统实现(C++实现)

cocos2dx-3.10  聊天系统实现(C++实现)

1.版权声明

    转载请注明出处:http://write.blog.csdn.net/postedit/51910754


2.前言

      一直忙着,没空写,趁中秋有点空,就码上奋斗

      本期介绍聊天系统制作,使用ListView和RichText 完成,本期重点是官方富文本RickText类的修改

       附上效果图:

        

3.内容

      介绍RichText的修改,由于官方的RickText 不支持描边、下划线、超链接,这里面将对官方RichText进行改造:
       因为用的是官方的预编译库,没有cpp源码,再就是为了不影响源码,就新建了自己的RichUI类。
      首先新建RichUI.h头文件,找到官方的UIRichText.h,拷贝内容至刚新建的RichUI.h头文件中,修改其中几处内容:
      将namespace ui改下,避免和官方冲突,主要做的修改的在RichElementText里,这里贴上我修改好的:
      
#ifndef __RICHUI_H__
#define __RICHUI_H__

#include "ui/UIWidget.h"
#include "cocos2d.h"
USING_NS_CC;

NS_CC_BEGIN
/**
* @addtogroup cui
* @modify source code by iuoon@zhejiang.china
*/

namespace cui {

	/**
	*@brief Rich text element base class.
	* It defines the basic common properties for all rich text element.
	*/
	class  RichElement : public Ref
	{
	public:
		/**
		*@brief Rich element type.
		*/
		enum class Type
		{
			TEXT,
			IMAGE,
			CUSTOM,
			NEWLINE
		};

		/**
		* @brief Default constructor.
		* @js ctor
		* @lua new
		*/
		RichElement() {};

		/**
		* @brief Default destructor.
		* @js NA
		* @lua NA
		*/
		virtual ~RichElement() {};


		/**
		* @brief Initialize a rich element with different arguments.
		*
		* @param tag A integer tag value.
		* @param color A color in @see `Color3B`.
		* @param opacity A opacity value in `GLubyte`.
		* @return True if initialize success, false otherwise.
		*/
		bool init(int tag, const Color3B& color, GLubyte opacity);
	protected:
		Type _type;
		int _tag;
		CC_SYNTHESIZE(std::string, _pramas, Pramas);
		Color3B _color;
		GLubyte _opacity;
		friend class RichText;
	};

	/**
	*@brief Rich element for displaying text.
	*/
	class  RichElementText : public RichElement
	{
	public:

		/**
		*@brief Default constructor.
		* @js ctor
		* @lua new
		*/
		RichElementText() { 
			_type = Type::TEXT;
			_outLine = -1;
			_outLineColor = Color4B::BLACK;
			_shadow = false;
			_linkurl = "";
			_underLinecolor = Color4B(0, 0, 0, 0);
			_underLinesize = -1;
			_touchCallback = nullptr;
		};


		/**
		*@brief Default destructor.
		* @js NA
		* @lua NA
		*/
		virtual ~RichElementText() {};

		/**
		* @brief Initialize a RichElementText with various arguments.
		*
		* @param tag A integer tag value.
		* @param color A color in Color3B.
		* @param opacity A opacity in GLubyte.
		* @param text Content string.
		* @param fontName Content font name.
		* @param fontSize Content font size.
		* @return True if initialize scucess, false otherwise.
		*/
		bool init(int tag, const Color3B& color, GLubyte opacity, const std::string& text, const std::string& fontName, float fontSize);


		/**
		* @brief Create a RichElementText with various arguments.
		*
		* @param tag A integer tag value.
		* @param color A color in Color3B.
		* @param opacity A opacity in GLubyte.
		* @param text Content string.
		* @param fontName Content font name.
		* @param fontSize Content font size.
		* @return RichElementText instance.
		*/
		static RichElementText* create(int tag, const Color3B& color, GLubyte opacity, const std::string& text, const std::string& fontName, float fontSize);
	
		void setTouchCallBack(std::function<void(std::string)> touch, std::string tag);
		void setLinkUrl(std::string linkurl);
	
	protected:
		std::string _text;
		std::string _fontName;
		float _fontSize;
		friend class RichText;
		/**************************************/
	protected:
//		CC_SYNTHESIZE(Color4B, _textColor, TextColor); //设置字体颜色
		CC_SYNTHESIZE(int, _outLine, OutLine);  //描边大小
		CC_SYNTHESIZE(Color4B, _outLineColor, OutLineColor);//描边颜色
		CC_SYNTHESIZE(bool, _shadow, Shadow); //启用阴影
		CC_SYNTHESIZE_READONLY(std::string, _linkurl, LinkUrl);//设置链接(或点击事件当参数传输)
		CC_SYNTHESIZE(Color4B, _underLinecolor, UnderLineColor);//下划线颜色
		CC_SYNTHESIZE(int, _underLinesize, UnderLineSize); //下划线大小
		CC_SYNTHESIZE_READONLY(std::function<void(std::string)>, _touchCallback, TouchCallBack);//点击回调函数

	private:
		void linkCallback(std::string str);

	};

	/**
	*@brief Rich element for displaying images.
	*/
	class  RichElementImage : public RichElement
	{
	public:

		/**
		* @brief Default constructor.
		* @js ctor
		* @lua new
		*
		*/
		RichElementImage() { _type = Type::IMAGE; };


		/**
		* @brief Default destructor.
		* @js NA
		* @lua NA
		*/
		virtual ~RichElementImage() {};


		/**
		* @brief Initialize a RichElementImage with various arguments.
		*
		* @param tag A integer tag value.
		* @param color A color in Color3B.
		* @param opacity A opacity in GLubyte.
		* @param filePath A image file name.
		* @return True if initialize success, false otherwise.
		*/
		bool init(int tag, const Color3B& color, GLubyte opacity, const std::string& filePath);


		/**
		* @brief Create a RichElementImage with various arguments.
		*
		* @param tag A integer tag value.
		* @param color A color in Color3B.
		* @param opacity A opacity in GLubyte.
		* @param filePath A image file name.
		* @return A RichElementImage instance.
		*/
		static RichElementImage* create(int tag, const Color3B& color, GLubyte opacity, const std::string& filePath);
	protected:
		std::string _filePath;
		Rect _textureRect;
		int _textureType;
		friend class RichText;
	};

	/**
	*@brief Rich element for displaying custom node type.
	*/
	class  RichElementCustomNode : public RichElement
	{
	public:

		/**
		* @brief Default constructor.
		* @js ctor
		* @lua new
		*/
		RichElementCustomNode() { _type = Type::CUSTOM; };


		/**
		* @brief Default destructor.
		* @js NA
		* @lua NA
		*/
		virtual ~RichElementCustomNode() { CC_SAFE_RELEASE(_customNode); };


		/**
		* @brief Initialize a RichElementCustomNode with various arguments.
		*
		* @param tag A integer tag value.
		* @param color A color in Color3B.
		* @param opacity A opacity in GLubyte.
		* @param customNode A custom node pointer.
		* @return True if initialize success, false otherwise.
		*/
		bool init(int tag, const Color3B& color, GLubyte opacity, Node* customNode);

		/**
		* @brief Create a RichElementCustomNode with various arguments.
		*
		* @param tag A integer tag value.
		* @param color A color in Color3B.
		* @param opacity A opacity in GLubyte.
		* @param customNode A custom node pointer.
		* @return A RichElementCustomNode instance.
		*/
		static RichElementCustomNode* create(int tag, const Color3B& color, GLubyte opacity, Node* customNode);
	protected:
		Node* _customNode;
		friend class RichText;
	};

	/**
	*@brief Rich element for new line.
	*/
	class  RichElementNewLine : public RichElement
	{
	public:

		/**
		* @brief Default constructor.
		* @js ctor
		* @lua new
		*
		*/
		RichElementNewLine() { _type = Type::NEWLINE; };

		/**
		* @brief Default destructor.
		* @js NA
		* @lua NA
		*/
		virtual ~RichElementNewLine() {};

		/**
		* @brief Create a RichElementNewLine with various arguments.
		*
		* @param tag A integer tag value.
		* @param color A color in Color3B.
		* @param opacity A opacity in GLubyte.
		* @return A RichElementNewLine instance.
		*/
		static RichElementNewLine* create(int tag, const Color3B& color, GLubyte opacity);
	protected:
		friend class RichText;
	};

	/**
	*@brief A container for displaying various RichElements.
	* We could use it to display texts with images easily.
	*/
	class  RichText : public ui::Widget
	{
	public:

		/**
		* @brief Default constructor.
		* @js ctor
		* @lua new
		*/
		RichText();

		/**
		* @brief Default destructor.
		* @js NA
		* @lua NA
		*/
		virtual ~RichText();

		/**
		* @brief Create a empty RichText.
		*
		* @return RichText instance.
		*/
		static RichText* create();

		/**
		* @brief Insert a RichElement at a given index.
		*
		* @param element A RichElement type.
		* @param index A given index.
		*/
		void insertElement(RichElement* element, int index);

		/**
		* @brief Add a RichElement at the end of RichText.
		*
		* @param element A RichElement instance.
		*/
		void pushBackElement(RichElement* element);

		/**
		* @brief Remove a RichElement at a given index.
		*
		* @param index A integer index value.
		*/
		void removeElement(int index);

		/**
		* @brief Remove specific RichElement.
		*
		* @param element A RichElement type.
		*/
		void removeElement(RichElement* element);

		/**
		* @brief Set vertical space between each RichElement.
		*
		* @param space Point in float.
		*/
		void setVerticalSpace(float space);

		/**
		* @brief Rearrange all RichElement in the RichText.
		* It's usually called internally.
		*/
		void formatText();

		virtual void visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags) override;

		//override functions.
		virtual void ignoreContentAdaptWithSize(bool ignore) override;
		virtual std::string getDescription() const override;

	CC_CONSTRUCTOR_ACCESS:
		virtual bool init() override;
		virtual void onEnter() override;
		virtual void onExit() override;

	protected:
		virtual void adaptRenderers() override;

		virtual void initRenderer() override;
		void pushToContainer(Node* renderer);
		void handleTextRenderer(const RichElementText& textInfo);
	//	void handleTextRenderer(const std::string& text, const std::string& fontName, float fontSize, const Color3B& color, GLubyte opacity);
		void handleImageRenderer(const std::string& fileParh, const Color3B& color, GLubyte opacity);
		void handleCustomRenderer(Node* renderer);
		void formarRenderers();
		void addNewLine();

		bool onTouchBegan(Touch *touch, Event *unusedEvent);
		void onTouchEnded(Touch *touch, Event *unusedEvent);

		CC_SYNTHESIZE(int, _touchPriority, TouchPriority);
	protected:
		bool _formatTextDirty;
		Vector<RichElement*> _richElements;
		std::vector<Vector<Node*>*> _elementRenders;
		std::map<Node*, std::function<void(std::string)> > _touchDelegate;//定义node对应的事件
		float _leftSpaceWidth;
		float _verticalSpace;

	};

}

NS_CC_END

#endif /* defined(__RICHUI_H__) */



      同样新建RichUI.cpp文件,将UIRichText.cpp中的源码拷贝至RichUI.cpp文件中,修改部分代码日下:
#include "ui/RichUI.h"
#include "ui/UIHelper.h"

NS_CC_BEGIN

namespace cui {


	bool RichElement::init(int tag, const Color3B &color, GLubyte opacity)
	{
		_tag = tag;
		_color = color;
		_opacity = opacity;
		return true;
	}


	RichElementText* RichElementText::create(int tag, const Color3B &color, GLubyte opacity, const std::string& text, const std::string& fontName, float fontSize)
	{
		RichElementText* element = new (std::nothrow) RichElementText();
		if (element && element->init(tag, color, opacity, text, fontName, fontSize))
		{
			element->autorelease();
			return element;
		}
		CC_SAFE_DELETE(element);
		return nullptr;
	}

	bool RichElementText::init(int tag, const Color3B &color, GLubyte opacity, const std::string& text, const std::string& fontName, float fontSize)
	{
		if (RichElement::init(tag, color, opacity))
		{
			_text = text;
			_fontName = fontName;
			_fontSize = fontSize;
			return true;
		}
		return false;
	}

	void RichElementText::setTouchCallBack(std::function<void(std::string)> touch, std::string pramas)
	{
		_touchCallback = touch;
		_pramas = pramas;
	}

	void RichElementText::setLinkUrl(std::string linkurl)
	{
		_linkurl = linkurl;
		setTouchCallBack(std::bind(&RichElementText::linkCallback, this, std::placeholders::_1), linkurl);
	}

	void RichElementText::linkCallback(std::string str)
	{
		log("call open url %s", str.c_str());
	}

	RichElementImage* RichElementImage::create(int tag, const Color3B &color, GLubyte opacity, const std::string& filePath)
	{
		RichElementImage* element = new (std::nothrow) RichElementImage();
		if (element && element->init(tag, color, opacity, filePath))
		{
			element->autorelease();
			return element;
		}
		CC_SAFE_DELETE(element);
		return nullptr;
	}

	bool RichElementImage::init(int tag, const Color3B &color, GLubyte opacity, const std::string& filePath)
	{
		if (RichElement::init(tag, color, opacity))
		{
			_filePath = filePath;
			return true;
		}
		return false;
	}

	RichElementCustomNode* RichElementCustomNode::create(int tag, const Color3B &color, GLubyte opacity, cocos2d::Node *customNode)
	{
		RichElementCustomNode* element = new (std::nothrow) RichElementCustomNode();
		if (element && element->init(tag, color, opacity, customNode))
		{
			element->autorelease();
			return element;
		}
		CC_SAFE_DELETE(element);
		return nullptr;
	}

	bool RichElementCustomNode::init(int tag, const Color3B &color, GLubyte opacity, cocos2d::Node *customNode)
	{
		if (RichElement::init(tag, color, opacity))
		{
			_customNode = customNode;
			_customNode->retain();
			return true;
		}
		return false;
	}

	RichElementNewLine* RichElementNewLine::create(int tag, const Color3B& color, GLubyte opacity)
	{
		RichElementNewLine* element = new (std::nothrow) RichElementNewLine();
		if (element && element->init(tag, color, opacity))
		{
			element->autorelease();
			return element;
		}
		CC_SAFE_DELETE(element);
		return nullptr;
	}

	RichText::RichText() :
		_formatTextDirty(true),
		_leftSpaceWidth(0.0f),
		_verticalSpace(0.0f),
		_touchPriority(-1)
	{
		_touchDelegate.clear();
	}

	RichText::~RichText()
	{
		_richElements.clear();
		//
		std::map<Node*, std::function<void(std::string)> >::const_iterator it = _touchDelegate.begin();
		while (it != _touchDelegate.end())
		{
			Node* node = it->first;
			if (node->getUserData() != nullptr)
			{
				delete (std::string*)(node->getUserData());
				node->setUserData(nullptr);
			}
			++it;
		}
		_touchDelegate.clear();
	}

	/***************可以重写改造换行******************/
	RichText* RichText::create()
	{
		RichText* widget = new (std::nothrow) RichText();
		if (widget && widget->init())
		{
			widget->autorelease();
			return widget;
		}
		CC_SAFE_DELETE(widget);
		return nullptr;
	}

	bool RichText::init()
	{
		if (Widget::init())
		{
			return true;
		}
		return false;
	}

	void RichText::onEnter()
	{
		Widget::onEnter();
		EventListenerTouchOneByOne* listener = EventListenerTouchOneByOne::create();
		listener->setSwallowTouches(true);
		listener->onTouchBegan = CC_CALLBACK_2(RichText::onTouchBegan, this);
		listener->onTouchEnded = CC_CALLBACK_2(RichText::onTouchEnded, this);
		_eventDispatcher->addEventListenerWithFixedPriority(listener, _touchPriority);
	}

	void RichText::onExit()
	{
		Widget::onExit();
		_eventDispatcher->removeAllEventListeners();
	}

	void RichText::initRenderer()
	{

	}

	void RichText::insertElement(RichElement *element, int index)
	{
		_richElements.insert(index, element);
		_formatTextDirty = true;
	}

	void RichText::pushBackElement(RichElement *element)
	{
		_richElements.pushBack(element);
		_formatTextDirty = true;
	}

	void RichText::removeElement(int index)
	{
		_richElements.erase(index);
		_formatTextDirty = true;
	}

	void RichText::removeElement(RichElement *element)
	{
		_richElements.eraseObject(element);
		_formatTextDirty = true;
	}

	void RichText::formatText()
	{
		if (_formatTextDirty)
		{
			this->removeAllProtectedChildren();
			_elementRenders.clear();
			if (_ignoreSize)
			{
				addNewLine();
				for (ssize_t i = 0; i<_richElements.size(); i++)
				{
					RichElement* element = _richElements.at(i);
					Node* elementRenderer = nullptr;
					switch (element->_type)
					{
					case RichElement::Type::TEXT:
					{
						Label* elementLabel = nullptr;
						RichElementText* elmtText = static_cast<RichElementText*>(element);
						if (FileUtils::getInstance()->isFileExist(elmtText->_fontName))
						{
							elementLabel = Label::createWithTTF(elmtText->_text.c_str(), elmtText->_fontName, elmtText->_fontSize);
						}
						else
						{
							elementLabel = Label::createWithSystemFont(elmtText->_text.c_str(), elmtText->_fontName, elmtText->_fontSize);
						}
						if (elmtText->getOutLine() > 0)
						{
							elementLabel->enableOutline(elmtText->getOutLineColor(), elmtText->getOutLine());
						}
						if (elmtText->getShadow())
						{
							elementLabel->enableShadow();
						}
						if (elmtText->getUnderLineSize() > 0)
						{
							LayerColor* l = nullptr;
							if (elmtText->getUnderLineColor().a == 0)
							{
								l = LayerColor::create(Color4B(elmtText->_color), elementLabel->getContentSize().width, elmtText->getUnderLineSize());
							}
							else
							{
								l = LayerColor::create(elmtText->getUnderLineColor(), elementLabel->getContentSize().width, elmtText->getUnderLineSize());
							}
							elementLabel->setUserObject(l);
						}
						if (elmtText->getTouchCallBack())
						{
							std::string* tag = new std::string(""+elmtText->getPramas());
							elementLabel->setUserData(tag);
							_touchDelegate[elementLabel] = elmtText->getTouchCallBack();
						}
						elementRenderer = elementLabel;
						
						break;
					}
					case RichElement::Type::IMAGE:
					{
						RichElementImage* elmtImage = static_cast<RichElementImage*>(element);
						elementRenderer = Sprite::create(elmtImage->_filePath.c_str());
						break;
					}
					case RichElement::Type::CUSTOM:
					{
						RichElementCustomNode* elmtCustom = static_cast<RichElementCustomNode*>(element);
						elementRenderer = elmtCustom->_customNode;
						break;
					}
					case RichElement::Type::NEWLINE:
					{
						addNewLine();
						break;
					}
					default:
						break;
					}
					elementRenderer->setColor(element->_color);
					elementRenderer->setOpacity(element->_opacity);
					pushToContainer(elementRenderer);
				}
			}
			else
			{
				addNewLine();
				for (ssize_t i = 0; i<_richElements.size(); i++)
				{

					RichElement* element = static_cast<RichElement*>(_richElements.at(i));
					switch (element->_type)
					{
					case RichElement::Type::TEXT:
					{
						RichElementText* elmtText = static_cast<RichElementText*>(element);
						handleTextRenderer(*elmtText);
						break;
					}
					case RichElement::Type::IMAGE:
					{
						RichElementImage* elmtImage = static_cast<RichElementImage*>(element);
						handleImageRenderer(elmtImage->_filePath.c_str(), elmtImage->_color, elmtImage->_opacity);
						break;
					}
					case RichElement::Type::CUSTOM:
					{
						RichElementCustomNode* elmtCustom = static_cast<RichElementCustomNode*>(element);
						handleCustomRenderer(elmtCustom->_customNode);
						break;
					}
					case RichElement::Type::NEWLINE:
					{
						addNewLine();
						break;
					}
					default:
						break;
					}
				}
			}
			formarRenderers();
			_formatTextDirty = false;
		}
	}

	void RichText::visit(Renderer * renderer, const Mat4 & parentTransform, uint32_t parentFlags)
	{
		if (_enabled)
		{
			formatText();
			Widget::visit(renderer, parentTransform, parentFlags);
		}
	}


	void RichText::handleTextRenderer(const RichElementText& textInfo)
	{
		auto fileExist = FileUtils::getInstance()->isFileExist(textInfo._fontName);
		Label* textRenderer = nullptr;
		if (fileExist)
		{
			textRenderer = Label::createWithTTF(textInfo._text, textInfo._fontName, textInfo._fontSize);
		}
		else
		{
			textRenderer = Label::createWithSystemFont(textInfo._text, textInfo._fontName, textInfo._fontSize);
		}
		

		float textRendererWidth = textRenderer->getContentSize().width;
		_leftSpaceWidth -= textRendererWidth;
		if (_leftSpaceWidth < 0.0f)
		{
			float overstepPercent = (-_leftSpaceWidth) / textRendererWidth;
			std::string curText = textInfo._text;
			size_t stringLength = StringUtils::getCharacterCountInUTF8String(textInfo._text);
			int leftLength = stringLength * (1.0f - overstepPercent);

			// The adjustment of the new line position
			auto originalLeftSpaceWidth = _leftSpaceWidth + textRendererWidth;
			auto leftStr = ui::Helper::getSubStringOfUTF8String(curText, 0, leftLength);
			textRenderer->setString(leftStr);
			auto leftWidth = textRenderer->getContentSize().width;
			if (originalLeftSpaceWidth < leftWidth) {
				// Have protruding
				for (;;) {
					leftLength--;
					leftStr = ui::Helper::getSubStringOfUTF8String(curText, 0, leftLength);
					textRenderer->setString(leftStr);
					leftWidth = textRenderer->getContentSize().width;
					if (leftWidth <= originalLeftSpaceWidth) {
						break;
					}
					else if (leftLength <= 0) {
						break;
					}
				}
			}
			else if (leftWidth < originalLeftSpaceWidth) {
				// A wide margin
				for (;;) {
					leftLength++;
					leftStr = ui::Helper::getSubStringOfUTF8String(curText, 0, leftLength);
					textRenderer->setString(leftStr);
					leftWidth = textRenderer->getContentSize().width;
					if (originalLeftSpaceWidth < leftWidth) {
						leftLength--;
						break;
					}
					else if (stringLength <= leftLength) {
						break;
					}
				}
			}

			//The minimum cut length is 1, otherwise will cause the infinite loop.
			if (0 == leftLength) leftLength = 1;
			std::string leftWords = ui::Helper::getSubStringOfUTF8String(curText, 0, leftLength);
			std::string cutWords = ui::Helper::getSubStringOfUTF8String(curText, leftLength, stringLength - leftLength);
			if (leftLength > 0)
			{
				Label* leftRenderer = nullptr;
				if (fileExist)
				{
					leftRenderer = Label::createWithTTF(ui::Helper::getSubStringOfUTF8String(leftWords, 0, leftLength), textInfo._fontName, textInfo._fontSize);
				}
				else
				{
					leftRenderer = Label::createWithSystemFont(ui::Helper::getSubStringOfUTF8String(leftWords, 0, leftLength), textInfo._fontName, textInfo._fontSize);
				}
				if (leftRenderer)
				{
					leftRenderer->setColor(textInfo._color);
					leftRenderer->setOpacity(textInfo._opacity);
					if (textInfo.getOutLine() > 0)
					{
						leftRenderer->enableOutline(textInfo.getOutLineColor(), textInfo.getOutLine());
					}
					if (textInfo.getShadow())
					{
						leftRenderer->enableShadow();
					}
					if (textInfo.getUnderLineSize() > 0)
					{
						LayerColor* l = nullptr;
						if (textInfo.getUnderLineColor().a == 0)
						{
							l = LayerColor::create(Color4B(textInfo._color), leftRenderer->getContentSize().width, textInfo.getUnderLineSize());
						}
						else
						{
							l = LayerColor::create(textInfo.getUnderLineColor(), leftRenderer->getContentSize().width, textInfo.getUnderLineSize());
						}
						leftRenderer->setUserObject(l);
					}
					if (textInfo.getTouchCallBack())
					{
						std::string* tag = new std::string(textInfo.getPramas());
						leftRenderer->setUserData(tag);
						_touchDelegate[leftRenderer] = textInfo.getTouchCallBack();
					}

					pushToContainer(leftRenderer);
				}
			}

			addNewLine();
			RichElementText cutRich = textInfo;
			cutRich._text=cutWords;
			handleTextRenderer(cutRich);
		}
		else
		{
			textRenderer->setColor(textInfo._color);
			textRenderer->setOpacity(textInfo._opacity);

			if (textInfo.getOutLine() > 0)
			{
				textRenderer->enableOutline(textInfo.getOutLineColor(), textInfo.getOutLine());
			}
			if (textInfo.getShadow())
			{
				textRenderer->enableShadow();
			}
			if (textInfo.getUnderLineSize() > 0)
			{
				LayerColor* l = nullptr;
				if (textInfo.getUnderLineColor().a == 0)
				{
					l = LayerColor::create(Color4B(textInfo._color), textRenderer->getContentSize().width, textInfo.getUnderLineSize());
				}
				else
				{
					l = LayerColor::create(textInfo.getUnderLineColor(), textRenderer->getContentSize().width, textInfo.getUnderLineSize());
				}
				textRenderer->setUserObject(l);
			}
			if (textInfo.getTouchCallBack())
			{
				std::string* tag = new std::string(textInfo.getPramas());
				textRenderer->setUserData(tag);
				_touchDelegate[textRenderer] = textInfo.getTouchCallBack();
			}

			pushToContainer(textRenderer);
		}
	}

	void RichText::handleImageRenderer(const std::string& fileParh, const Color3B &color, GLubyte opacity)
	{
		Sprite* imageRenderer = Sprite::create(fileParh);
		handleCustomRenderer(imageRenderer);
	}

	void RichText::handleCustomRenderer(cocos2d::Node *renderer)
	{
		Size imgSize = renderer->getContentSize();
		_leftSpaceWidth -= imgSize.width;
		if (_leftSpaceWidth < 0.0f)
		{
			addNewLine();
			pushToContainer(renderer);
			_leftSpaceWidth -= imgSize.width;
		}
		else
		{
			pushToContainer(renderer);
		}
	}

	void RichText::addNewLine()
	{
		_leftSpaceWidth = _customSize.width;
		_elementRenders.push_back(new Vector<Node*>());
	}

	bool RichText::onTouchBegan(Touch * touch, Event * unusedEvent)
	{
		std::map<Node*, std::function<void(std::string)> >::const_iterator it = _touchDelegate.begin();
		while (it != _touchDelegate.end())
		{
			Node* node = it->first;
			if (node->getBoundingBox().containsPoint(node->getParent()->convertTouchToNodeSpace(touch)))
			{
				return true;
			}
			++it;
		}
		return false;
	}

	void RichText::onTouchEnded(Touch * touch, Event * unusedEvent)
	{
		std::map<Node*, std::function<void(std::string)> >::const_iterator it = _touchDelegate.begin();
		while (it != _touchDelegate.end())
		{
			Node* node = it->first;
			if (node->getBoundingBox().containsPoint(node->getParent()->convertTouchToNodeSpace(touch)))
			{
				if (node->getUserData() != nullptr)
				{
					(it->second)(*((std::string*)node->getUserData()));
				}

				return;
			}
			++it;
		}
	}

	void RichText::formarRenderers()
	{
		if (_ignoreSize)
		{
			float newContentSizeWidth = 0.0f;
			float newContentSizeHeight = 0.0f;

			Vector<Node*>* row = (_elementRenders[0]);
			float nextPosX = 0.0f;
			for (ssize_t j = 0; j<row->size(); j++)
			{
				Node* l = row->at(j);
				l->setAnchorPoint(Vec2::ZERO);
				l->setPosition(nextPosX, 0.0f);
				this->addProtectedChild(l, 1);
				/****这里将下划线显示*************/
				Node* under = dynamic_cast<Node*>(l->getUserObject());
				if (under)
				{
					under->setPosition(Point(nextPosX, -1));
					this->addProtectedChild(under,1);
					l->setUserObject(nullptr);
				}
				/**********************************/
				Size iSize = l->getContentSize();
				newContentSizeWidth += iSize.width;
				newContentSizeHeight = MAX(newContentSizeHeight, iSize.height);
				nextPosX += iSize.width;
			}
			this->setContentSize(Size(newContentSizeWidth, newContentSizeHeight));
		}
		else
		{
			float newContentSizeHeight = 0.0f;
			float *maxHeights = new float[_elementRenders.size()];

			for (size_t i = 0; i<_elementRenders.size(); i++)
			{
				Vector<Node*>* row = (_elementRenders[i]);
				float maxHeight = 0.0f;
				for (ssize_t j = 0; j<row->size(); j++)
				{
					Node* l = row->at(j);
					maxHeight = MAX(l->getContentSize().height, maxHeight);
				}
				maxHeights[i] = maxHeight;
				newContentSizeHeight += maxHeights[i];
			}

			float nextPosY = _customSize.height;
			for (size_t i = 0; i<_elementRenders.size(); i++)
			{
				Vector<Node*>* row = (_elementRenders[i]);
				float nextPosX = 0.0f;
				nextPosY -= (maxHeights[i] + _verticalSpace);

				for (ssize_t j = 0; j<row->size(); j++)
				{
					Node* l = row->at(j);
					l->setAnchorPoint(Vec2::ZERO);
					l->setPosition(nextPosX, nextPosY);
					this->addProtectedChild(l, 1);
					/****这里将下划线显示*************/
					Node* under = dynamic_cast<Node*>(l->getUserObject());
					if (under)
					{
						under->setPosition(Point(nextPosX, nextPosY - 1));
						this->addProtectedChild(under,1);
						l->setUserObject(nullptr);
					}
					/**********************************/
					nextPosX += l->getContentSize().width;
				}
			}
			delete[] maxHeights;
		}

		size_t length = _elementRenders.size();
		for (size_t i = 0; i<length; i++)
		{
			Vector<Node*>* l = _elementRenders[i];
			l->clear();
			delete l;
		}
		_elementRenders.clear();

		if (_ignoreSize)
		{
			Size s = getVirtualRendererSize();
			this->setContentSize(s);
		}
		else
		{
			this->setContentSize(_customSize);
		}
		updateContentSizeWithTextureSize(_contentSize);
	}

	void RichText::adaptRenderers()
	{
		this->formatText();
	}

	void RichText::pushToContainer(cocos2d::Node *renderer)
	{
		if (_elementRenders.size() <= 0)
		{
			return;
		}
		_elementRenders[_elementRenders.size() - 1]->pushBack(renderer);
	}

	void RichText::setVerticalSpace(float space)
	{
		_verticalSpace = space;
	}

	void RichText::ignoreContentAdaptWithSize(bool ignore)
	{
		if (_ignoreSize != ignore)
		{
			_formatTextDirty = true;
			Widget::ignoreContentAdaptWithSize(ignore);
		}
	}

	std::string RichText::getDescription() const
	{
		return "RichText";
	}

	

}

NS_CC_END

   到了接近成功的一半了,下面新建ChatUI,这里面需要注意的是,RichUI需要设置尺寸大小的,这里需要根据发送内容动态调整RichUI的尺寸大小,
  否则如果发送内容过长,我们的消息就会叠到一块了。
 
#pragma once
#ifndef _CHAT_UI_H_
#define _CHAT_UI_H_

#include "cocos2d.h"
#include <string.h>
#include "ui/RichUI.h"

USING_NS_CC;
using namespace std;
using namespace cocos2d::cui;

class ChatUI :public cocos2d::Layer
{
public:
	ChatUI();
	~ChatUI();

	static cocos2d::Scene* createScene();

	virtual bool init();

	CREATE_FUNC(ChatUI);

	void initRichEdit();

	cui::RichText* getChatMsg(int channel, string roleName, string chatMsg, char* signs);

private:
	cui::RichText* _richBugle;//喇叭

};



#endif

#include "ui/ChatUI.h"
#include "util/FontChina.h"
#include "ui/UIText.h"


ChatUI::ChatUI()
{
}

ChatUI::~ChatUI()
{
}

cocos2d::Scene * ChatUI::createScene()
{
	auto scene = Scene::create();
	auto layer = ChatUI::create();
	scene->addChild(layer);
	return scene;
}


bool ChatUI::init()
{
	if (!Layer::init())
	{
		return false;
	}
	this->initRichEdit();
	return true;
}


void ChatUI::initRichEdit()
{
	
}

cui::RichText* ChatUI::getChatMsg(int channel, string  roleName, string  chatMsg, char * signs)
{
	string chanStr = "【当前】";
	string siz = ":";
	int msglen = chatMsg.size() + siz.size()+ chanStr.size() + roleName.size();
	int s = msglen / 44;
	if (msglen%44>0)
	{
		s += 1;
	}
	cui::RichText* _richChat = cui::RichText::create();
	_richChat->ignoreContentAdaptWithSize(false);
	if (s==1)
	{
		_richChat->setContentSize(Size(268, 18 * s));
	}
	else {
		_richChat->setContentSize(Size(268, 15 * s+3));
	}
		
	RichElementText* res = new RichElementText();
	RichElementText* resrole = new RichElementText();
	
	if (channel==1)
	{
		chanStr = "【世界】";
		res = RichElementText::create(1, Color3B::YELLOW, 255, FontChina::getStringUTF8(chanStr), "font/simkai.ttf", 15);
	}
	else if (channel == 2) {
		chanStr = "【地区】";
		res = RichElementText::create(1, Color3B::ORANGE, 255, FontChina::getStringUTF8(chanStr), "font/simkai.ttf", 15);
	}
	else if (channel == 3) {
		chanStr = "【系统】";
		res = RichElementText::create(1, Color3B::RED, 255, FontChina::getStringUTF8(chanStr), "font/simkai.ttf", 15);
	}
	else {
		res = RichElementText::create(1, Color3B::WHITE, 255, FontChina::getStringUTF8(chanStr), "font/simkai.ttf", 15);
	}
	resrole = RichElementText::create(1, Color3B::GREEN, 255, FontChina::getStringUTF8(roleName), "font/simkai.ttf", 15);
	resrole->setUnderLineSize(1);
	resrole->setUnderLineColor(Color4B::GREEN);
	auto fuhao= RichElementText::create(1, Color3B::BLACK, 255, FontChina::getStringUTF8(":"), "font/simkai.ttf", 15);
	auto re = RichElementText::create(1, Color3B(0, 255, 255), 255, chatMsg , "font/simkai.ttf", 15);
	
	_richChat->pushBackElement(res);
	_richChat->pushBackElement(resrole);
	if (channel != 3)
	{
		_richChat->pushBackElement(fuhao);
	}	
	_richChat->pushBackElement(re);
	RichElementNewLine* line = RichElementNewLine::create(1,Color3B::WHITE,255);
	_richChat->pushBackElement(line);

	return _richChat;
}

新建MianScene,完成场景加载:
 
#ifndef _MAIN_SCENE_H_
#define _MAIN_SCENE_H_
#include "cocos2d.h"
#include "ui/CocosGUI.h"//UI相关的头文件
#include "cocostudio/CocoStudio.h"
#include "ui/ChatUI.h"

USING_NS_CC;

class MainScene :public cocos2d::Layer
{

public:
	MainScene();
	~MainScene();

	static cocos2d::Scene* createScene();

	virtual bool init();

	CREATE_FUNC(MainScene);

	void sendChatMsg(Ref * pSender, ui::Widget::TouchEventType type);


public:
	Node* _ui_node;
	ChatUI* _chat;
	ui::TextField* _textfield;
	ui::ListView* _listview;
	int index;
	cui::RichText* _text;

};


#endif

#include "scene/MainScene.h"
#include "YijianScene.h"
#include "util/FontChina.h"


MainScene::MainScene()
{
	index = 1;
}

MainScene::~MainScene()
{
}


cocos2d::Scene * MainScene::createScene()
{
	auto scene = Scene::create();

	auto layer = MainScene::create();

	scene->addChild(layer);
	return scene;
}

bool MainScene::init()
{
	if (!Layer::init())
	{
		return false;
	}
	Size visibleSize = Director::getInstance()->getVisibleSize();
	Vec2 origin = Director::getInstance()->getVisibleOrigin();

	_ui_node = CSLoader::createNode("scene/UILayer.csb");
	this->addChild(_ui_node, 200,100);

	_listview = static_cast<ui::ListView*>(_ui_node->getChildByName("ListView_1"));
	_listview->setBright(true);

	auto enterbt = static_cast<ui::Button*>(_ui_node->getChildByName("Button_3"));
	enterbt->addTouchEventListener(CC_CALLBACK_2(MainScene::sendChatMsg, this));

	//从服务端获取角色所在地图,这里默认加载
	auto yjlayer = YijianScene::createScene();
	this->addChild(yjlayer, 1,200);
	
	_chat = ChatUI::create();
	_text = _chat->getChatMsg(3, "", FontChina::getStringUTF8("欢迎【醉不忆情丝】上线,登陆IP地址:192.168.1.118"), "");
	_listview->insertCustomItem(_text, 0);


	return true;
}

void MainScene::sendChatMsg(Ref * pSender, ui::Widget::TouchEventType type)
{
	switch (type)
	{
	case cocos2d::ui::Widget::TouchEventType::BEGAN:
		break;
	case cocos2d::ui::Widget::TouchEventType::MOVED:
		break;
	case cocos2d::ui::Widget::TouchEventType::ENDED:{
		_textfield = static_cast<ui::TextField*>(_ui_node->getChildByName("TextField_1"));
		if (_textfield->getString().compare("")!=0)
		{
			_text = _chat->getChatMsg(0, "醉不忆情丝", _textfield->getString(), "");
			_listview->insertCustomItem(_text,index);
			_listview->sortAllChildren();
			_listview->jumpToBottom();//将最后显示在底部
			log("send message");
			index += 1;
		}
		
		break;
		}
	case cocos2d::ui::Widget::TouchEventType::CANCELED:
		break;
	default:
		break;
	}
}

 就这样,聊天初步完成。

 未完待续。
      

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值