这是很久以前写的笔记,很乱,找时间重新优化一下。
#ifndef _HALL_MSG_PARSER_H
#define _HALL_MSG_PARSER_H
#include "cocos2d.h"
USING_NS_CC;
using namespace std;
/**
isWhole �Ƿ���һ�����壬���Dz�ֳ�һ����һ��labelTTF��
color ������ɫ
fontSize���ִ�С
font ��������
image��ǩ��url��/resourceĿ¼�¡�
const char* str1 = "<sentences><characters isWhole=\"1\" color=\"00FFFF\" fontSize=\"28\" font=\"Microsoft YaHei\">[ϵͳ]</characters>
<characters isWhole=\"1\" color=\"FF00FF\" fontSize=\"28\" font=\"Microsoft YaHei\">����ţ��</characters>
<image>player_girl.png</image>
<characters isWhole=\"1\" color=\"FFFF00\" fontSize=\"28\" font=\"Microsoft YaHei\">�õ���</characters>
<characters isWhole=\"0\" color=\"CCAABB\" fontSize=\"28\" font=\"Microsoft YaHei\">��̽�ռұ��䡿x1</characters>
</sentences>";
�÷���createһ��HallMsgParser��Ȼ��parseMsgFromXMLStrAndShow(const char*,CCSize)������һ��CClayer����Ϣ���������Layer�д����ң����ϵ�����䡣
һ��HallMsgParserʵ������parser���char*��ÿ�η���һ���µ�CCLayer
�����ַ���
< С�ں� ʹ��<ʵ���滻 ������;��
> ���ں� >
& �� &
�� ������(Ӣ��) '������\'
" ˫���ţ�Ӣ�ģ� "������\"
*/
class HallMsgParser : public CCObject, public CCSAXDelegator
{
public:
CREATE_FUNC(HallMsgParser);
HallMsgParser();
~HallMsgParser();
bool init();
bool reset();
CCLayer* parseMsgFromXMLStrAndShow(const char* xml,CCSize size);
void attachToLayer(CCLayer* layer,vector<CCNode*> contents);
int utf8_char_len(char firstByte);
/**
*��Ӧxml��ǩ��ʼ,�磺<string name="alex">, nameΪstring��attsΪstring�����ԣ���["name","alex"]
*/
virtual void startElement(void *ctx, const char *name, const char **atts);
/**
*��Ӧxml��ǩ����,�磺</string>
*/
virtual void endElement(void *ctx, const char *name);
/**
*��Ӧxml��ǩ�ı�,�磺<string name="alex">Alex Zhou</string>��Alex Zhou
*/
virtual void textHandler(void *ctx, const char *s, int len);
private:
bool parseWithString(const char* content);
bool clearTmpData();
private:
vector<CCNode*> contents;
ccColor3B color;
string startXMLElement;
string endXMLElement;
//���ֵ�����
string font;
string tmp_txt;
//���ֵ�����<characters isWhole="1",color="FFFFFF",fontSize="28",font="Microsoft YaHei"> [ϵͳ] </characters>
//�����Ƿ�Ӧ��Ϊһ�����壬��һ��label��ʾ����[ϵͳ][С��ñ]:�����ţ��Ҳ����衣������е�[ϵͳ]��[С��ñ]��Ϊһ�����壬�������Ҳ����裬����һ����һ��label��
int isWhole;
//������ɫ
//���ִ�С
int fontSize;
char* m_key;
//�������Ľڵ��Ƿ������֣�true�����֣�false��ͼƬ
bool isCharacter;
};
#endif
#include "comm\HallMsgParser.h"
#include "IconvString.h"
using namespace std;
// 空格
const static int SPACE = 32;
// 换行
const static int NEXTLINE = 10;
// tab 横向制表符
const static int TAB = 9;
//默认的字体颜色rgb组成
const static int R_BY_DEFAULT = 0xFF;
const static int G_BY_DEFAULT = 0xFF;
const static int B_BY_DEFAULT = 0xFF;
//默认的字体大小
const static int FONTSIZE_BY_DEFAULT = 18;
//默认一条消息的消息框最大行数
const static int LINE_MAX_BY_DEFAULT = 2;
//消息默认的字间距
const static float INTERVAL_X_BY_DEFAULT = 0;
//消息的默认行间距
const static float INTERVAL_Y_BY_DEFAULT = 0;
//默认字体
const static char FONT_BY_DEFAULT[] = "Microsoft YaHei";
//文字节点,<characters ...>...</characters>
const static char CHARACTERS_TAG[] = "characters";
//图片节点.<image>...<image>
const static char IMG_TAG[] = "image";
const static char ATTR_IS_WHOLE_TAG[] = "isWhole";
const static char ATTR_COLOR_TAG[] = "color";
const static char ATTR_FONT_SIZE_TAG[] = "fontSize";
const static char ATTR_FONT_TAG[] = "font";
const unsigned char kFirstBitMask = 128; // 1000000
const unsigned char kSecondBitMask = 64; // 0100000
const unsigned char kThirdBitMask = 32; // 0010000
const unsigned char kFourthBitMask = 16; // 0001000
const unsigned char kFifthBitMask = 8; // 0000100
const static std::string EMPTY_STR = "";
//默认字体颜色
const static ccColor3B FONT_COLOR_BY_DEFAULT = ccWHITE;
HallMsgParser::HallMsgParser()
{
}
HallMsgParser::~HallMsgParser()
{
CCLog("**********~HallMsgParser**********");
reset();
CCLog("**********reset()**********");
}
//只取了size中的width。
CCLayer* HallMsgParser::parseMsgFromXMLStrAndShow(const char* xml,CCSize size)
{
bool isSuccess = false;
if(!xml||size.width == 0)
{
CCLog(" xml string is null or mayby the width of CCSize is 0");
return NULL;
}
CCLayer* tmpLayer = CCLayer::create();
do
{
CC_BREAK_IF(NULL == tmpLayer);
tmpLayer->setContentSize(size);
CCLog("***********ready to parse string works*************");
CC_BREAK_IF(!(isSuccess = parseWithString(xml)));
CCLog("*********** parse string successfully *************");
//解析成功,把解析出的数据填充到layer中。
attachToLayer(tmpLayer,contents);
}
while(0);
if(!isSuccess)
{
CCLog("***************parse string failed*************");
}
reset();
return tmpLayer;
}
void HallMsgParser::attachToLayer(CCLayer* layer,vector<CCNode*> contents)
{
CCLog("**********attachToLayer**********");
if(layer == NULL||contents.size() == 0)
{
CCLOG("*****attach to layer failed!*****");
return;
}
//文字的高度取决于最高的那个CCNode。
float height_max = -1;
//图层的位置
CCPoint layerPos = layer->getPosition();
//图层高度,宽度
float layer_height = layer->getContentSize().height;
float layer_width = layer->getContentSize().width;
//图层最左边x坐标
float left_max = layerPos.x - (layer_width/2);
//图层最顶端y坐标
float top_max = layerPos.y + (layer_height/2);
CCLog("********** content size is ::%d",contents.size());
for (unsigned int i = 0;i<contents.size(); i++)
{
float tmp = contents[i]->getContentSize().height;
height_max = height_max < tmp ? tmp : height_max;
}
CCLog("********** highest item of contents is :: %f",height_max);
//当前已经填充到第几行了。
int curLineTmp = 1;
//在当前行已经占用了多宽的空间。
int currWidthTmp = 0;
float unit_height = 0;
float unit_width = 0;
float unitPosX = 0;
float unitPosY = 0;
for (unsigned int i = 0; i<contents.size(); i++)
{
CCLog("********** current line is %d **********",curLineTmp);
unit_height = contents[i]->getContentSize().height;
unit_width = contents[i]->getContentSize().width;
//是否可以在当前行填充当前子label,否则应该另起一行
if(currWidthTmp + unit_width > layer_width && unit_width <= layer_width)
{
if(curLineTmp == LINE_MAX_BY_DEFAULT)
{
break;
}
//填充行+1
curLineTmp++;
//另起一行,当前填充的空间需要清0
currWidthTmp = 0;
}
//一个labelttf的宽度都超过了屏幕宽度,干脆不要显示了,丢弃。
if(unit_width > layer_width)
{
CCLog("********** the width of current unit is larger than layer_width,won't dispear. unitWidth:%f**********",unit_width);
continue;
}
unitPosX = left_max + currWidthTmp + unit_width/2 + INTERVAL_X_BY_DEFAULT;
unitPosY = top_max - (height_max + INTERVAL_Y_BY_DEFAULT) * (curLineTmp - 1) - unit_height/2 - INTERVAL_Y_BY_DEFAULT;
CCLog("********** position of unit %d: %f,%f**********",i,unitPosX,unitPosY);
contents[i]->setPosition(CCPointMake(unitPosX,unitPosY));
currWidthTmp += unit_width;
layer->addChild(contents[i]);
}
//重新设置消息框宽高,
layer->setContentSize(CCSize(layer_width,curLineTmp * height_max));
}
bool HallMsgParser::parseWithString(const char *content)
{
bool isSuccess = false;
if(!content)
{
CCLog("********NULL content*******");
return isSuccess;
}
CCSAXParser _parse;
_parse.setDelegator(this);
#if(CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
//gbk转utf-8
size_t inLen = strlen(content);
size_t outLen = (inLen << 1);//转到utf-8存储,字节数放大2倍。
//记得free
char *outBuf = (char *)malloc(outLen);
//把传入的xml一律转换成utf-8
gbk2utf8((char *)content, inLen, outBuf, outLen);
isSuccess = _parse.parse(outBuf, strlen(outBuf));
free(outBuf);
#else
isSuccess = _parse.parse(content, strlen(content));
#endif
return isSuccess;
}
bool HallMsgParser::reset()
{
clearTmpData();
contents.clear();
return true;
}
bool HallMsgParser::clearTmpData()
{
//解析前先初始化一些相关数据
m_key = NULL;
startXMLElement = EMPTY_STR;
endXMLElement = EMPTY_STR;
//默认把解析到的节点看成是文字节点
isCharacter = true;
//默认解析到的节点的内容是整体的。因为有种情况是:必须把解析到的一串文字一个文字一个label这样拆分。
isWhole = true;
//默认的字体颜色
color = FONT_COLOR_BY_DEFAULT;
font = FONT_BY_DEFAULT;
fontSize = FONTSIZE_BY_DEFAULT;
tmp_txt = EMPTY_STR;
//contents.clear();
return true;
}
bool HallMsgParser::init()
{
return reset();
}
void HallMsgParser::startElement(void *ctx, const char *name, const char **atts)
{
startXMLElement = (char *)name;
CCLog("********** start= %s **********", startXMLElement.c_str());
//节点名称
m_key = (char *)name;
//文字
if(0 == strcmp(CHARACTERS_TAG,m_key))
{
//遍历这个文字节点,获取所有相关属性
while(atts && *atts)
{
//节点属性名称
const char* attrKey = *atts;
//右移一个单位,指向属性值
++atts;
//属性值
const char* attrValue = *atts;
//CCLog("***********%s::%s****************",attrKey,attrValue);
if(attrValue && *attrValue)
{
if(0 == strcmp(ATTR_IS_WHOLE_TAG,attrKey))
{
isWhole = atoi(attrValue);
CCLog("*******isWhole::%d*******",isWhole);
}
else if(0 == strcmp(ATTR_COLOR_TAG,attrKey))
{
if(strlen(attrValue) == 6)
{
//tmp = "00FFFF",接续00,ff,ff
const string tmp = attrValue;
int r = R_BY_DEFAULT;
int g = G_BY_DEFAULT;
int b = B_BY_DEFAULT;
sscanf((tmp.substr(0,2)).c_str(),"%x",&r);
sscanf((tmp.substr(2,2)).c_str(),"%x",&g);
sscanf((tmp.substr(4,2)).c_str(),"%x",&b);
color = ccc3(r,g,b);
CCLog("*****r::%c,g::%c,b::%c*******",color.r,color.g,color.b);
}
}
else if(0 == strcmp(ATTR_FONT_SIZE_TAG,attrKey))
{
fontSize = atoi(attrValue);
CCLog("*******fontSize::%d*******",fontSize);
}
else if(0 == strcmp(ATTR_FONT_TAG,attrKey))
{
font = attrValue;
CCLog("*******font::%s*******",font.c_str());
}
}
//CCLOG("*****%s::%s*****",startXMLElement,attrKey);
atts++;
}
//防止值异常
isWhole==1||isWhole==0?0:isWhole=1;
fontSize>0?0:fontSize=18;
isCharacter = true;
}
//解析到的节点是图片
else if(0 == strcmp(IMG_TAG,m_key))
{
isCharacter = false;
}
CCLog("*******isCharacter::%d*******",isCharacter);
}
void HallMsgParser::endElement(void *ctx, const char *name)
{
endXMLElement = (char *)name;
CCLog("********** end= %s **********", endXMLElement.c_str());
if(isWhole ==1 && 0 != strcmp(tmp_txt.c_str(),EMPTY_STR.c_str()))
{
do
{
CCLog("*****content:%s,*****fontName:%s,*****fontSize:%d,*****",tmp_txt.c_str(),font.c_str(),fontSize);
CCLabelTTF* unit = CCLabelTTF::create(tmp_txt.c_str(),font.c_str(),fontSize);
CC_BREAK_IF(NULL == unit);
unit->setColor(color);
contents.push_back(unit);
}while(0);
}
clearTmpData();
//还原所有值到默认值
}
void HallMsgParser::textHandler(void *ctx, const char *s, int len)
{
CCLog("****************txtHandle***************");
//文字内容,或者image的url
string value((char *)s, 0, len);
string tmp(EMPTY_STR);
//是否全是非正常字符
bool noValue = true;
for(int i = 0; i < len; ++i)
{
if(s[i] != SPACE && s[i] != NEXTLINE && s[i] != TAB)
{
noValue = false;
break;
}
}
if(noValue) return;
//文字
if(isCharacter)
{
//所有文字在一个label中
if(isWhole == 1)
{
tmp_txt += value;
CCLog("****** tmp_txt:: %s********",tmp_txt.c_str());
}
//一个文字一个label
else
{
for (unsigned int i = 0; i < value.length(); i++)
{
//utf-8编码,根据首字节,判断这个字符所占字节数
int offset = utf8_char_len(value[i]);
tmp = value.substr(i,offset);
i += (offset-1);
CCLog("*****content:%s,*****fontName:%s,fontSize:%d*****",tmp.c_str(),font.c_str(),fontSize);
CCLabelTTF* unit = CCLabelTTF::create(tmp.c_str(),font.c_str(),fontSize);
unit->setColor(color);
contents.push_back(unit);
tmp = EMPTY_STR;
}
}
}
//图片
else
{
do
{
CCLog("*****image url: %s*****",value.c_str());
CCSprite* image = CCSprite::create(value.c_str());
CC_BREAK_IF(NULL == image);
contents.push_back(image);
}while(0);
}
}
int HallMsgParser::utf8_char_len(char firstByte)
{
string::difference_type offset = 1;
if(firstByte & kFirstBitMask) // This means the first byte has a value greater than 127, and so is beyond the ASCII range.
{
if(firstByte & kThirdBitMask) // This means that the first byte has a value greater than 224, and so it must be at least a three-octet code point.
{
if(firstByte & kFourthBitMask) // This means that the first byte has a value greater than 240, and so it must be a four-octet code point.
offset = 4;
else
offset = 3;
}
else
{
offset = 2;
}
}
return offset;
}
继承自CCSAXDelegator,实现startElement(),endElement(),和textHandler()三个函数。解析出的结果存入CCdictionary中,一键值对存储。相当于一个map。
核心:三个过程。
virtual void startElement(void ctx, const char name, const char atts) = 0;
这是解析到一个以
virtual void endElement(void ctx, const char name) = 0;
这是解析到一个以
virtual void textHandler(void ctx, const char s, int len) = 0;
这是解析
注意:解析过程是:
1
6**
1 startelement
2 startelement
3 textHandler
4 endElement
5 textHandler,在解析完后,还会执行一次这个函数。
6 endElement