RichLabel基于Cocos2dx+Lua v3.x

RichLabel

简介


RichLabel基于Cocos2dx+Lua v3.x
解析字符串方面使用了labelparser,它能够将必定格式的字符串,转换为lua中的表结构
扩展标签极其简单,只需添加一个遵照规则的标签插件便可,无需改动已存在代码!!!html

(标签插件都在labels文件夹下)node

labelparser的详解
labelparser在github上的源码
RichLabel在github上的源码git

  • 支持图片(缩放,旋转,是否可见)
  • 支持文本属性(字体,大小,颜色,阴影,描边,发光)
  • 支持标签嵌套修饰文本,可是内部标签不会继承嵌套标签的属性
  • 支持标签扩展(labels文件夹中可添加标签支持)
  • 支持渐入动画,动画逐字回调
  • 支持设置最大宽度,自动换行布局
  • 支持手动换行,使用'\n'换行
  • 支持设置行间距,字符间距
  • 支持添加debug绘制文字范围和锚点
  • 支持得到文字的精灵节点
  • 支持设置标签锚点,透明度,颜色...
  • 支持遍历字符,行等
  • 支持得到任意行的行高

效果:github

------------------------------------------------------
------------  TEST RICH-LABEL
------------------------------------------------------ 

local test_text = {
    "<div fontcolor=#ff0000>hello</div><div fontcolor=#00ff00>hello</div><div fontsize=12>你</div><div fontSize=26 fontcolor=#ff00bb>好</div>ok",
    "<div outline=1,#ff0000 >hello</div>",
    "<div glow=#ff0000 >hello</div>",
    "<div shadow=2,-2,0.5,#ff0000 >hello</div>",
    "hello<img src='res/test.png' scale=0.5 rotate=90 visible=true />world",
}
for i=1, #test_text do
    local RichLabel = require("richlabel.RichLabel")
    local label = RichLabel.new {
        fontName = "res/msyh.ttf",
        fontSize = 20,
        fontColor = cc.c3b(255, 255, 255),
        maxWidth=200,
        lineSpace=0,
        charSpace=0,
    }
    label:setString(test_text[i])
    label:setPosition(cc.p(380,500-i*30))
    label:playAnimation()
    sceneGame:addChild(label)

    label:debugDraw()
end

     

     

 

因为解析字符串使用了labelparser,那么咱们先简单了解一下它,详细了解 传送门缓存

下面就是使用labelparser解析富文本串用法和产生的table格式,大概了解一下curl

local text1 = "hello worldd   <div>hello world</div> 你好 <div fontName='nihao' fontColore=#ff33ee>hello,world</div><div></div>"
local parsedtable = labelparser.parse(text1)
-- output:
<parsedtable> = {
    {
        content = "hello worldd   ",
        labelname = "div",
    },
    {
        content = "hello world",
        labelname = "div",
    },
    {
        content = " 你好 ",
        labelname = "div",
    },
    {
        content = "hello,world",
        fontname = "nihao",
        fontsize = "#123456",
        labelname = "div",
    },
}

这种格式十分方便咱们处理,它将每一个文本片断及其属性分拆成table,而后按顺序组织好返回给咱们
这样咱们处理就能够简单多了函数

原理


首先要说一下,这位前辈灵动君心他也有一个RichLabel,可是没法知足个人需求,大致思路和他相似。
大致思路:工具

1.解析字符串布局

2.解析出来的表中元素的处理post

  • 对于字符串的处理就是将字符串拆分红一个个字符,而后每一个字符建立一个Label
  • 对于图片的处理就是直接建立精灵

3.将建立好的node布局便可


 

第一步
解析字符串,解析字符串使用了个人另外一个开源工具labelparser,直接解析就能够返回table

第二步
咱们按照顺序从表第一项开始处理
每一项均可以得到对应的标签名,根据标签名调用对应的标签的处理函数,同时要将表项中的属性传入标签处理函数
(这个处理函数是以插件形式提供的,很便于扩展)
下面是img标签解析的的代码(label_img.lua)

--
-- <img/> 标签解析
--

return function (self, params, default)
    if not params.src then return 
    end
    -- 建立精灵,自动在帧缓存中查找,屏蔽了图集中加载和直接加载的区别
    local sprite = self:getSprite(params.src)
    if not sprite then
        self:printf("<img> - create sprite failde")
        return
    end
    if params.scale then
        sprite:setScale(params.scale)
    end
    if params.rotate then
        sprite:setRotation(params.rotate)
    end
    if params.visible ~= nil then
        sprite:setVisible(params.visible)
    end
    return {sprite}
end

第三步
此时咱们就得到了设置好属性的node,咱们要作的就是布局文本
首先咱们处理换行,何时换行呢?
+ 若是设置了MaxWidth那么,每行最大宽度不能超过MaxWidth,不然就换行
+ 若是文本内容中存在换行符\n,则直接换行

咱们遍历全部的node(node存在顺序)而后检测是否为Label,为Label则检测内容是否为\n,而后检测此时累加宽度若超过了最大宽度,则将当前的node直接放到下一行
代码

-- 自动适应换行处理方法,内部会根据最大宽度设置和'\n'自动换行
-- 若无最大宽度设置则不会自动换行
function RichLabel:adjustLineBreak_(allnodelist, charspace)
    -- 若是maxwidth等于0则不自动换行
    local maxwidth = self._maxWidth
    if maxwidth <= 0 then maxwidth = 999999999999
    end
    -- 存放每一行的nodes
    local alllines = {{}, {}, {}}
    -- 当前行的累加的宽度
    local addwidth = 0
    local rowindex = 1
    local colindex = 0
    for _, node in pairs(allnodelist) do
        colindex = colindex + 1
        -- 为了防止存在缩放后的node
        local box = node:getBoundingBox()
        addwidth = addwidth + box.width
        local totalwidth = addwidth + (colindex - 1) * charspace
        local breakline = false
        -- 若累加宽度大于最大宽度
        -- 则当前元素为下一行第一个元素
        if totalwidth > maxwidth then
            rowindex = rowindex + 1
            addwidth = box.width -- 累加数值置当前node宽度(为下一行第一个)
            colindex = 1
            breakline = true
        end

        -- 在当前行插入node
        local curline = alllines[rowindex] or {}
        alllines[rowindex] = curline
        table.insert(curline, node)

        -- 若尚未换行,而且换行符存在,则下一个node直接转为下一行
        if not breakline and self:adjustContentLinebreak_(node) then
            rowindex = rowindex + 1
            colindex = 0
            addwidth = 0 -- 累加数值置0
        end
    end
    return alllines
end

-- 判断是否为文本换行符
function RichLabel:adjustContentLinebreak_(node)
    -- 若为Label则有此方法
    if node.getString then
        local str = node:getString() 
        -- 查看是否为换行符
        if str == "\n" then
            return true
        end
    end
    return false
end

 

这样咱们就将混在一块的node拆分红一个table中存一行
虽然咱们知道哪些node在第一行,哪些在第二行... ...
可是咱们尚未布局呢!!!
下面咱们就遍历每一行,而后调用行布局函数layoutLine_, 行累加函数还返回行的真实宽度和高度,这样咱们就能够计算出最宽的一行,即为RichLabel的宽度

精简后代码

for index, line in pairs(alllines) do
    local linewidth, lineheight = self:layoutLine_(basepos, line, 1, charspace)
    -- todo
end

 

行布局函数(精简后)

-- 布局单行中的节点的位置,并返回行宽和行高
function RichLabel:layoutLine_(basepos, line, anchorpy, charspace)
    local pos_x = basepos.x
    local pos_y = basepos.y
    local lineheight = 0
    local linewidth = 0
    for index, node in pairs(line) do
        local box = node:getBoundingBox()
        -- 设置位置
        node:setPosition((pos_x + linewidth + box.width/2), pos_y)
        -- 累加行宽度
        linewidth = linewidth + box.width + charspace
        -- 查找最高的元素,为行高
        if lineheight < box.height then lineheight = box.height
        end
    end
    return linewidth, lineheight
end

 

这样咱们就一行行布局好了

工具函数


问题:文本标签处理函数,首先要先将字符串拆分红一个个字符,若是字符串中存在中文那么直接拆分确定是不行的

拆分字符串,支持Unicode编码

function RichLabel:stringToChars(str)
    -- 主要用了Unicode(UTF-8)编码的原理分隔字符串
    -- 简单来讲就是每一个字符的第一位定义了该字符占据了多少字节
    -- UTF-8的编码:它是一种变长的编码方式
    -- 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。所以对于英语字母,UTF-8编码和ASCII码是相同的。
    -- 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一概设为10。
    -- 剩下的没有说起的二进制位,所有为这个符号的unicode码。
    local list = {}
    local len = string.len(str)
    local i = 1 
    while i <= len do
        local c = string.byte(str, i)
        local shift = 1
        if c > 0 and c <= 127 then
            shift = 1
        elseif (c >= 192 and c <= 223) then
            shift = 2
        elseif (c >= 224 and c <= 239) then
            shift = 3
        elseif (c >= 240 and c <= 247) then
            shift = 4
        end
        local char = string.sub(str, i, i+shift-1)
        i = i + shift
        table.insert(list, char)
    end
    return list, len
end

 

问题:处理颜色也要说一下,因为使用HTML方式标记颜色,因此要解析#FF0099这种类型的颜色

这里须要注意返回的是cc.c4b ,由于咱们可能使用颜色设置Label阴影,而Label的阴影函数要求cc.c4b,可是若是传入cc.c3b的话,alpha值会为0,结果就是没效果!!!

对于这种状况的详细讨论见 Cocos2dx+lua中Color参数的坑 

-- 解析16进制颜色rgb值
function  RichLabel:convertColor(xstr)
    if not xstr then return 
    end
    local toTen = function (v)
        return tonumber("0x" .. v)
    end

    local b = string.sub(xstr, -2, -1) 
    local g = string.sub(xstr, -4, -3) 
    local r = string.sub(xstr, -6, -5)

    local red = toTen(r)
    local green = toTen(g)
    local blue = toTen(b)
    if red and green and blue then 
        return cc.c4b(red, green, blue, 255)
    end
end

 

问题:由于也支持了图片,因此图片的加载必需要考虑,不管是从图集中加载仍是碎图加载都应该正常

-- 建立精灵,如今帧缓存中找,没有则直接加载
-- 屏蔽了使用图集和直接使用碎图建立精灵的不一样
function RichLabel:getSprite(filename)
    local spriteFrameCache = cc.SpriteFrameCache:getInstance()
    local spriteFrame = spriteFrameCache:getSpriteFrameByName(filename)

    if spriteFrame then
        return cc.Sprite:createWithSpriteFrame(spriteFrame)
    end
    return cc.Sprite:create(filename)
end

 

要详细了解仍是去看看代码吧!!!

相关文章
相关标签/搜索