cocos2dx-3.10 聊天系统实现(C++实现)
2016-07-14 16:45
639 查看
cocos2dx-3.10 聊天系统实现(C++实现)
1.版权声明
转载请注明出处:http://write.blog.csdn.net/postedit/519107542.前言
一直忙着,没空写,趁中秋有点空,就码上。
本期介绍聊天系统制作,使用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; } }
就这样,聊天初步完成。
未完待续。
相关文章推荐
- cocos 3.8.1新建c++或者lua项目 及其运行
- Cocos2d-x 事件分发机制
- cocospod相关
- Cocos2d-x 如何优化内存使用
- Cocos2d-x 纹理缓存(Texture Cache)
- Cocos2d-x 内存管理——引用计数
- Cocos2d-x 声音和音效
- 001-Cocos2dx-2.1.3环境搭建-windows
- cocos Eclipse 打包。问题记录==
- 【步兵 cocos2dx】图片管理 之 什么时候打pvr.ccz
- Cocos2d-x 图像渲染和动画——裁剪(ClippingNode)
- Cocos2d-x 图像渲染和动画——粒子系统
- Cocos2d-x 图像渲染和动画——场景转换(Transitions)
- Cocos2d-x 图像渲染和动画——序列帧动画
- cocos2dx spine之二 :spine变色
- Cocos2d-x 图像渲染和动画——动作
- Cocos2d-x 图像渲染和动画——3.0坐标系详解
- Cocos2d-x 基础概念——调度器(scheduler)
- Cocos2d-x 基础概念——导演、场景、层、精灵
- Cocos2d-x 综述——API 风格