cocos2dx 3.17.2 lua RichText使用


做聊天时需使用富文本功能实现不同颜色文本,下划线,根据固定宽度自动换行,点击部分文本触发一些事件等由此想到了引擎自带的richtext

之前的版本也用过richtext由于诸多问题以及功能不多选择的都是网上用node或者label实现的富文本,这个版本查看了api文档后发现大部分功能以及实现了,那就不需要自己造轮子了

但是实际使用时发现诸多问题,特此记录一下,做备忘也给其他需要的朋友参考

1.ccui.RichText可以根据xml格式初始化,实现了大部分我需要的功能,格式放在下面,或者直接去我参考的作者链接https://blog.csdn.net/qq_26701531/article/details/81634336

<font face='xxx.ttf' size='24' color='#0000ff'>textfont>
 <small>textsmall>
 <big>textbig>
 <u>textu> 下划线/
 <i>texti> 斜体字/
 <b>textb> 加粗/
 <del>textdel> 中划线/
 <img src='cocosui/sliderballnormal.png'  width='30' height='30'/> 图片 /
 <a href='http://www.google.com'>click me text a> 链接 /
 <br/> 换行  == /n /
 <outline color=\"#D2B48C\" size=\"2\">text 描边/
 =\"#4169E1\" offsetWidth=\"8\" offsetHeight=\"-8\" blurRadius=\"2\">text 阴影/
 =\"#AFEEEE\">text 外发光/

2.RichText:createWithXML (xml,defaults,handleOpenUrl)使用时发现三个参数,第一个传xml的字符串(上文提到的那些),第二个是默认的富文本格式,第三个为打开url的回调,

由于lua这边没有太多信息,去翻看c++源码发现,除了第一个参数必传之外,另外两个参数都是有默认值的,

(1)lua这边提示必须传2个以上参数,修改lua_cocos2dx_ui_auto.cpp文件里面对应的函数参数,增加只传一个参数的选项

(2)使用时发现不管第三个参数如何传都会被断言卡住,查看lua_cocos2dx_ui_auto.cpp时发现如下代码

do {
    // Lambda binding for lua is not supported.
    assert(false);
} while(0)
         ;

提示 Lambda binding for lua is not supported.然后就没绑定,修改之后增加绑定

修改以上两点后的代码如下(去lua_cocos2dx_ui_auto.cpp里面搜索lua_cocos2dx_ui_RichText_createWithXML,红色部分为我增加和修改)

int lua_cocos2dx_ui_RichText_createWithXML(lua_State* tolua_S)
{
    int argc = 0;
    bool ok  = true;

#if COCOS2D_DEBUG >= 1
    tolua_Error tolua_err;
#endif

#if COCOS2D_DEBUG >= 1
    if (!tolua_isusertable(tolua_S,1,"ccui.RichText",0,&tolua_err)) goto tolua_lerror;
#endif

    argc = lua_gettop(tolua_S) - 1;
    
    if (argc == 1)
    {
        std::string arg0;
        ok &= luaval_to_std_string(tolua_S, 2,&arg0, "ccui.RichText:createWithXML");
        if(!ok)
        {
            tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_ui_RichText_createWithXML'", nullptr);
            return 0;
        }
        cocos2d::ui::RichText* ret = cocos2d::ui::RichText::createWithXML(arg0);
        object_to_luaval(tolua_S, "ccui.RichText",(cocos2d::ui::RichText*)ret);
        return 1;
    }
    if (argc == 2)
    {
        std::string arg0;
        cocos2d::ValueMap arg1;
        ok &= luaval_to_std_string(tolua_S, 2,&arg0, "ccui.RichText:createWithXML");
        ok &= luaval_to_ccvaluemap(tolua_S, 3, &arg1, "ccui.RichText:createWithXML");
        if(!ok)
        {
            tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_ui_RichText_createWithXML'", nullptr);
            return 0;
        }
        cocos2d::ui::RichText* ret = cocos2d::ui::RichText::createWithXML(arg0, arg1);
        object_to_luaval(tolua_S, "ccui.RichText",(cocos2d::ui::RichText*)ret);
        return 1;
    }
    if (argc == 3)
    {
        std::string arg0;
        cocos2d::ValueMap arg1;
        std::function<void (const std::string&)> arg2;
        ok &= luaval_to_std_string(tolua_S, 2,&arg0, "ccui.RichText:createWithXML");
        ok &= luaval_to_ccvaluemap(tolua_S, 3, &arg1, "ccui.RichText:createWithXML");
        
        // do {
            // // Lambda binding for lua is not supported.
            // assert(false);
        // } while(0)
        // ;
    
#if COCOS2D_DEBUG >= 1
        if (!toluafix_isfunction(tolua_S,4,"LUA_FUNCTION",0,&tolua_err) )
        {
            goto tolua_lerror;
        }
#endif
  
        LUA_FUNCTION handler = toluafix_ref_function(tolua_S,4,0);
        arg2 = [handler](std::string str) {
            LuaStack* stack = LuaEngine::getInstance()->getLuaStack();
            stack->pushString(str.c_str());
            stack->executeFunctionByHandler(handler, 1);
        };

        if(!ok)
        {
            tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_ui_RichText_createWithXML'", nullptr);
            return 0;
        }
        cocos2d::ui::RichText* ret = cocos2d::ui::RichText::createWithXML(arg0, arg1, arg2);
        object_to_luaval(tolua_S, "ccui.RichText",(cocos2d::ui::RichText*)ret);
        return 1;
    }
    luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ccui.RichText:createWithXML",argc, 1);
    return 0;
#if COCOS2D_DEBUG >= 1
    tolua_lerror:
    tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_ui_RichText_createWithXML'.",&tolua_err);
#endif
    return 0;
}

3.使用handleOpenUrl回调时发现一个问题,在空白场景时好用,去了我的弹窗界面等就不好用了,经过排查后发现是因为UIRichText.cpp里面触发handleOpenUrl的触摸为多点触摸,但是底层的机制为单点触摸优先多点,实际使用时各种ui界面肯定会吞噬触摸,防止界面点穿,这就导致,还没触发多点触摸已经被吞噬了,后来没有办法只能修改handleOpenUrl触发为单点触摸,不知道引擎为啥会给它使用多点触摸,

修改方法打开UIRichText.cpp搜索EventListenerTouchAllAtOnce(里面就只有一个,或者搜索ListenerComponent),将该多点触摸改成单点EventListenerTouchOneByOne,其他的代码就是注册相应的监听了,我就不贴出来了,有一点需要注意,因为单点的默认优先级都为0,我们使用富文本时一般都是最后添加的,所以优先级好像也不高,我就手动给他设置优先级为-1(越小优先级越高)addEventListenerWithSceneGraphPriority改为addEventListenerWithFixedPriority,onTouchesEnded改为onTouchEnded然后在修改下参数已经实现就好,编译完之后发现好用了

实际使用时我是通过handleOpenUrl传回来的字符串(richtext初始化时传入的url)进行判断,然后实现不同的逻辑跳转等(懒人做法),如果有需要可以自己增加参数

4.使用时会发现富文本的高度和大小获取的不对,其实时使用方式不对,贴个示例记录下

local testStr = "用户名升星成功,获得8星英雄测试令人羡慕"
   
    local callback = function (str)
        print(str)
        local strTab = string.split(str, "|")
        if strTab[1] == "username" then
            showToast("我是" .. strTab[2])
        elseif strTab[1] == "heroname" then
            showToast("我是" .. strTab[2])
        elseif strTab[1] == "goodsname" then
            showToast("我是" .. strTab[2])
        end
    end 

    local def = { KEY_VERTICAL_SPACE,
        KEY_WRAP_MODE,
        KEY_HORIZONTAL_ALIGNMENT,
        KEY_FONT_COLOR_STRING,
        KEY_FONT_SIZE, KEY_FONT_FACE,
        KEY_ANCHOR_FONT_COLOR_STRING,
        KEY_ANCHOR_TEXT_BOLD,
        KEY_ANCHOR_TEXT_ITALIC,
        KEY_ANCHOR_TEXT_LINE,
        KEY_ANCHOR_TEXT_STYLE,
        KEY_ANCHOR_TEXT_OUTLINE_COLOR,
        KEY_ANCHOR_TEXT_OUTLINE_SIZE,
        KEY_ANCHOR_TEXT_SHADOW_COLOR,
        KEY_ANCHOR_TEXT_SHADOW_OFFSET_WIDTH,
        KEY_ANCHOR_TEXT_SHADOW_OFFSET_HEIGHT,
        KEY_ANCHOR_TEXT_SHADOW_BLUR_RADIUS,
        KEY_ANCHOR_TEXT_GLOW_COLOR,
    }
    local test_label = ccui.RichText:createWithXML(testStr,def,callback)

    local ElementText_Type = {
        ITALICS_FLAG = 1,         --/*!< italic text */
        BOLD_FLAG = 2,             --/*!< bold text */
        UNDERLINE_FLAG= 4,       -- /*!< underline */
        STRIKETHROUGH_FLAG= 8,    --/*!< strikethrough */
        URL_FLAG= 16,             -- /*!< url of anchor */
        OUTLINE_FLAG= 32,         -- /*!< outline effect */
        SHADOW_FLAG= 64,           --/*!< shadow effect */
        GLOW_FLAG= 128            -- /*!< glow effect */
    }

    local flags = 0;

    flags = bit._or(flags,ElementText_Type.UNDERLINE_FLAG)
    flags = bit._or(flags,ElementText_Type.URL_FLAG)
    
    local element = ccui.RichElementText:create(1,cc.RED,255,"抽奖券",'res/font/bmjy.ttf',30,flags,"goodsname|抽奖券")
    test_label:pushBackElement(element)

local width = 500 --test_label:formatText(); --该行代码之后获取大小才准 --local size = test_label:getContentSize() --local height = math.ceil(size.width / width) * size.height --test_label:ignoreContentAdaptWithSize(false) --test_label:setContentSize(width,height)
    test_label:formatText(); --该行后面设置大小获取大小才准确      test_label:ignoreContentAdaptWithSize(false)      test_label:setContentSize(width,0)

    createSprite(self.layout_buttom,"common_txt_bg11.png",UI_TEX_TYPE_PLIST,width,height,cc.p(360,200))
    test_label:setPosition(cc.p(360,200))
    test_label:addTo(self.layout_buttom)

5.后续发现下划线颜色问题,一直显示白色,是由于label的下划线颜色_displayedColor替换了之前_textColor,不随字的颜色走了,但是富文本这边设置颜色只是设置了setTextColor字的颜色,导致下划线颜色一直默认白色,也没有参数能获取富文本内部的label,以及设置下划线颜色,我为了快,就把c++里面setTextColor换成了setColor这样就修改了_displayedColor,字和下划线颜色一样了(懒人做法,我的需求是够用了),如有需要最好是自己增加个标签或者函数来传参修改颜色

至此基本功能算是都好用了,我就去撸代码了,后续有其他的功能其实可以通过RichElementCustomNode实现,像图片以及发光,描边这些参数里带着的就不说了,有点啰嗦了