cocos2dx 3.3rc版本中 RichText类 当自定义了RichText的宽度, 对于字符串的截取有问题,如果中文和英文数字混合的时候,会导致 要么 一行还有位置 就换行了,要么 显示不全。 看了下RichText实现,
重点是handlerTextRenderer方法
void RichText::handleTextRenderer(const std::string& text,const std::string& fontName,float fontSize,const Color3B &color,GLubyte opacity) { auto fileExist = FileUtils::getInstance()->isFileExist(fontName); Label* textRenderer = nullptr; if (fileExist) { textRenderer = Label::createWithTTF(text,fontName,fontSize); }else if(FileUtils::getInstance()->isFileExist(Label::getCustomFont())){ textRenderer = Label::createWithTTF(text,Label::getCustomFont(),fontSize); } else { textRenderer = Label::createWithSystemFont(text,fontSize); } float textRendererWidth = textRenderer->getContentSize().width; _leftSpaceWidth -= textRendererWidth; if (_leftSpaceWidth < 0.0f) { <span style="color:#FF0000;">float overstepPercent = (-_leftSpaceWidth) / textRendererWidth; std::string curText = text; size_t stringLength = StringUtils::getCharacterCountInUTF8String(text); int leftLength = stringLength * (1.0f - overstepPercent);</span> std::string leftWords = Helper::getSubStringOfUTF8String(curText,leftLength); std::string cutWords = Helper::getSubStringOfUTF8String(curText,leftLength,stringLength - leftLength); if (leftLength > 0) { Label* leftRenderer = nullptr; if (fileExist) { leftRenderer = Label::createWithTTF(Helper::getSubStringOfUTF8String(leftWords,leftLength),fontSize); } else if (FileUtils::getInstance()->isFileExist(Label::getCustomFont())) { leftRenderer = Label::createWithTTF(Helper::getSubStringOfUTF8String(leftWords,fontSize); } else { leftRenderer = Label::createWithSystemFont(Helper::getSubStringOfUTF8String(leftWords,fontSize); } if (leftRenderer) { leftRenderer->setColor(color); leftRenderer->setOpacity(opacity); pushToContainer(leftRenderer); } } addNewLine(); handleTextRenderer(cutWords.c_str(),fontSize,color,opacity); } else { textRenderer->setColor(color); textRenderer->setOpacity(opacity); pushToContainer(textRenderer); } }关键是标红的那几句代码: 他是用 当前行空余宽度 除以 文字纹理的长度得出的百分比 再乘以字符串长度得出字符个数,除出来的float值 用int 接收,直接丢弃掉小数部分,看似没有问题。实际上, 用百分比乘出来的宽度默认 认为 所有字符的宽度都是一样的, 所以当英文 数字混合的时候 就会导致 还有空余放的下 英文, 它却直接换行了。
还有个问题,就是 int 去接收 float类型的数据 可能导致浮点误差 比如 我 空余宽度除以文字纹理长度得出的百分比是 0.215054,文字字符个数是93, 我摁计算器算出来的都是20.000022, 用int 接收应该是20, 但是这里的出来的结果竟然是19??? 让人莫名其妙。 实际上 我知道 这是 类型运算的转换 可能导致的浮点误差 导致的。但是也没有理解到底问题在哪里。
起初想到的是 Label 指定了Label的宽度的时候,Label纹理宽度超过了指定宽度的时候 它也有自动换行的操作,但是看了Label的实现,觉得Label 写的可读性太差了,很难看懂呀。我看到 Label对 字符位置的更新是在 alignText这个方法里, 有个LabelTextFormatter:createStringSprite 的静态方法。
LabelTextFormatter::createStringSprites(this); if(_maxLineWidth > 0 && _contentSize.width > _maxLineWidth && LabelTextFormatter::multilineText(this) ) LabelTextFormatter::createStringSprites(this); if(_labelWidth > 0 || (_currNumLines > 1 && _hAlignment != TextHAlignment::LEFT)) LabelTextFormatter::alignText(this);看着就让人好费解, 先调用一次createStringSprites 判断条件 再调用一次。不知道它想干啥?
在createStringSprites中, 将字符串 创建出字符纹理,设置好坐标等, 但是 LabelTextFormatter::multilineText 这个方法看了几遍 愣是不知道它想干啥?
好, 回到 RichText。
其实 关键是要得到 leftLength这个 精确数值, 通过这个数值确定最终截取位置。
为了解决这个问题 看了下Label的实现, 无论是UIText 还是LabelTTF 最终渲染的 都是Label, Label 有 getLetter 这个接口,就可以 通过文字纹理拿到 当前的字符 ,从而拿到 当前字符的位置,然后再做精细调整。
最终结果就是:
if (_leftSpaceWidth < 0.0f) { float overstepPercent = (-_leftSpaceWidth) / textRendererWidth; std::string curText = text; size_t stringLength = StringUtils::getCharacterCountInUTF8String(text); int leftLength = stringLength * (1.0f - overstepPercent); Sprite *letter =textRenderer->getLetter( leftLength ); float pos_left_x_ = letter->getPositionX() - letter->getContentSize().width/2; float pos_right_x_ = letter->getPositionX() + letter->getContentSize().width/2; while( pos_left_x_ < _leftSpaceWidth+textRendererWidth ) { leftLength = leftLength + 1; letter =textRenderer->getLetter( leftLength ); if ( letter == NULL ){ break; } pos_left_x_ = letter->getPositionX() + letter->getContentSize().width/2; } while( pos_right_x_ > _leftSpaceWidth+textRendererWidth ){ leftLength = leftLength - 1; letter =textRenderer->getLetter( leftLength ); if ( letter == NULL ){ break; } pos_right_x_ = letter->getPositionX() + letter->getContentSize().width/2; } std::string leftWords = Helper::getSubStringOfUTF8String(curText,fontSize); } if (leftRenderer) { leftRenderer->setColor(color); leftRenderer->setOpacity(opacity); pushToContainer(leftRenderer); } }问题解决。 如果谁有更好的方案,求分享。