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

 

要詳細瞭解仍是去看看代碼吧!!!

相關文章
相關標籤/搜索