1. 程式人生 > >在NGUI的UILabel中使用動態字型與表情

在NGUI的UILabel中使用動態字型與表情

前言


眾所周知,UILabel的靜態字型是支援表情的,不過需要將表情與文字打包到一個圖集中,並且因為受限於靜態字型,幾乎不適用於中文日文等文字的圖文混排。

這幾天因為專案需要研究了一下動態字型中使用表情的解決方案,不過好像並沒有搜到比較好的方案,最終研究了一下NGUI中的原始碼,決定採取類似於UIInput生成游標和高亮的做法。

原理

原理基本上就是在UILabel每次處理文字的ProcessText方法中判斷當前文字是否含有表情符號,如果有,就在UILabel下新增一個UITexture,並將計算好的頂點座標和UV座標寫入UITexture中;另外還需要相應的修改NGUIText中計算頂點座標和UV座標的類,確保UILabel中文字的座標空出了表情的位置。

直觀一點描述就是,UILabel中只顯示文字,UITexture中顯示錶情,這兩個控制元件疊在一起就形成了圖文混排的效果。

從目前達到的效果來看還算不錯,需要顯示錶情時會動態建立UITexture,並不會帶來過多的記憶體開銷,另外我給UILabel加了useEmoji這個開關來控制是否檢查表情,預設為false,如果某些UILabel需要顯示錶情,就給設成true就好,當然也可以在程式碼中修改。

另外我又給UILabel加了emojiAlignment這個屬性,可以控制表情和文字的垂直對齊方式,預設的選項是Top,即表情和文字的上邊緣對齊;Center則垂直居中對齊;當然比較常見的應該是Bottom對齊,即下邊緣對齊。

使用

使用起來也比較方便,只需要實現你自己的EmojiProvider,並賦值給UILabel就可以了。

public abstract class EmojiProvider {
    public delegate bool EmojiFilter(string text, EmojiVO symbol);
    public EmojiFilter emojiFilter;


    public virtual Texture mainTexture {
        get {
            throw new System.NotImplementedException();
        }
    }

    public
abstract bool HasEmojis(string text); public BMSymbol MatchEmoji(string text, int offset = 0) { if (string.IsNullOrEmpty(text)) return null; return MatchEmoji(text, offset, text.Length - offset); } public abstract BMSymbol MatchEmoji(string text, int offset, int length); public abstract List<EmojiVO> GetAllEmojis (); }

下面是我實現的預設的EmojiProvider,邏輯很簡單,就是在初始化的時候從指定圖集中讀取所有圖片,並將[#圖片名]作為表情匹配的關鍵字,並且其中加了快取匹配結果的處理,因為NGUIText中預設匹配表情的處理有點腦殘,幾乎每一個字都會遍歷一遍所有圖片進行匹配,這點我懶得改了,直接快取匹配結果也一樣。

public class DefaultEmojiProvider : EmojiProvider
{
    string mAtlasPath = "";
    UIAtlas mAtlas;
    List<EmojiVO> mEmojis;
    Dictionary<int, EmojiVO> mEmojiMatchCache;
    string mLastMatchText = null;
    static DefaultEmojiProvider mInstance;
    public static DefaultEmojiProvider instance {
        get {
            if (mInstance == null) {
                mInstance = new DefaultEmojiProvider();
            }
            return mInstance;
        }
    }

    private DefaultEmojiProvider () {
        mAtlasPath = "Assets" + Path.DirectorySeparatorChar + "Atlases" + Path.DirectorySeparatorChar + "Emoji.prefab";
        mAtlas = AssetDatabase.LoadAssetAtPath<UIAtlas>(mAtlasPath);

        mEmojis = new List<EmojiVO>();
        mEmojiMatchCache = new Dictionary<int, EmojiVO>();
        LoadEmojis();
    }

    private void LoadEmojis() {
        if (mAtlas == null || Utils.IsNullOrEmpty(mAtlas.spriteList))
            return;

        var spriteList = mAtlas.spriteList;
        for (int i = 0; i < spriteList.Count; i++)
        {
            BMSymbol sym = new BMSymbol() { sequence = "[#" + spriteList[i].name + "]", spriteName = spriteList[i].name };
            mEmojis.Add(new EmojiVO(sym));
        }
    }
    // public override void 
    public override List<EmojiVO> GetAllEmojis()
    {
        return mEmojis;
    }

    public virtual void CheckEmojiMatches (string text) {
        mEmojiMatchCache.Clear();

        var emojis = GetAllEmojis();
        if (Utils.IsNullOrEmpty(emojis))
            return;

        string regexStr = "(";
        regexStr += Regex.Escape(emojis[0].symbol.sequence);
        for (int i = 1; i < emojis.Count; i++)
        {
            regexStr += "|" + Regex.Escape(emojis[i].symbol.sequence);
        }
        regexStr += ")";
        Regex regex = new Regex(regexStr);
        var match = regex.Match(text);
        while (match.Success){
            var emoji = emojis.Find(e => string.Equals(@e.symbol.sequence, @match.Value));
            if (emoji != null && emoji.symbol != null)
                mEmojiMatchCache.Add(match.Index, emoji);

            match = match.NextMatch();
        }
    }

    public override BMSymbol MatchEmoji(string text, int offset, int length)
    {
        if (string.IsNullOrEmpty(text) || length <= 0)
            return null;

        if (!string.Equals(mLastMatchText, text)) {
            mLastMatchText = text;
            CheckEmojiMatches(text);
        }

         if (!mEmojiMatchCache.ContainsKey(offset))
            return null;

        var emoji = mEmojiMatchCache[offset];
        if (length < emoji.symbol.sequence.Length)
            return null;

        if (emojiFilter != null && !emojiFilter (text, emoji)) {
            return null;
        }

        if (emoji.symbol.Validate(mAtlas))
            return emoji.symbol;

        return null;
    }

    public override bool HasEmojis(string text)
    {
        return !Utils.IsNullOrEmpty(mEmojis);
    }

    public override Texture mainTexture {
        get {
            if (mAtlas == null)
                return null;

            return mAtlas.texture;
        }
    }
}

這是EmojiProvider的賦值

    EmojiProvider mEmojiProvider;
    public EmojiProvider emojiProvider {
        get {
            if (!useEmojis)
                return null;

            if (mEmojiProvider == null) return DefaultEmojiProvider.instance;
            return mEmojiProvider;
        }
        set {
            if (!useEmojis)
                return;
            mEmojiProvider = value;
        }
    }

未解決的問題

這個專案基本可以滿足普通需求,但是暫時不支援多張貼圖,或者說多張圖集。

一張貼圖的大小通常是1024*1024,小圖足夠放個幾百個了,但是如果有幾個大圖,比如250*250大小的就只夠放16張,只支援一張貼圖顯然不夠。這個問題暫時還沒有解決,留待日後好了。

另外編輯模式下,更新表情的圖集會導致UV座標混亂,切換一下useEmoji,或者執行一下游戲就好了。

上兩張效果圖:
這裡寫圖片描述
這裡寫圖片描述

PS:我使用的NGUI版本是3.8.2,其中比較重要的修改是UILabel,NGUIText,UILabelInspector這三個檔案。

相關推薦

在NGUI的UILabel使用動態字型表情

前言 眾所周知,UILabel的靜態字型是支援表情的,不過需要將表情與文字打包到一個圖集中,並且因為受限於靜態字型,幾乎不適用於中文日文等文字的圖文混排。 這幾天因為專案需要研究了一下動態字型中使用表情的解決方案,不過好像並沒有搜到比較好的方案,最終研究

web---JSP動態include靜態include的區別?

1. 動態include <jsp:include page="目標jsp"> 它的原理是使用了 request.getRequestDispatcher(目標jsp).include(request,response) 來實現頁面包含,其本質是將 源jsp 和 目標

jsp動態include靜態include的區別-面試題

     JSP中動態INCLUDE與靜態INCLUDE的區別 動態INCLUDE用jsp:include動作實現 <jsp:include page="included.jsp" flush="true">它總是會檢查所含檔案中的變化,適合用於包含動態頁面,並

C++動態型別動態繫結、虛擬函式、執行時多型的實現【轉】

(轉自:https://blog.csdn.net/iicy266/article/details/11906509) 動態型別與靜態型別 靜態型別          是指不需要考慮表示式的執行期語

Vue 動態元件 v-once 指令

toggle 兩個元件的功能,程式碼如下。 <div id="root"> <child-one v-if="type === 'child-one'"></child-one> <child-two v-if="type === 'child

html元素動態新增刪除

<div class="unit" > <label>產品引數</label> <input type="button" value="新增" onclick="addProduc

mybatis動態sql的實現使用

首先引用一段mybatis文件中的話: 動態 SQL     MyBatis 的強大特性之一便是它的動態 SQL。如果你有使用 JDBC 或其它類似框架的經驗,你就能體會到根據不同條件拼接 SQL 語句的痛苦。例如拼接時要確保不能忘記新增必要的空格,還要注意去掉列表最

css字型段落屬性設定/文字高階樣式

CSS中字型與段落屬性 毫無疑問,不管什麼網站,文字一定是必不可少。文字可以是網頁傳播資訊的主要手段。那麼怎麼顯示文字,才能更加的美觀,那麼大家需要了解以下文字屬性。   字型屬性 屬性 用途 語法(一些寫法)

c語言記憶體的動態分配釋放(多維動態陣列構建)

一. 靜態陣列與動態陣列    靜態陣列比較常見,陣列長度預先定義好,在整個程式中,一旦給定大小後就無法再改變長度,靜態陣列自己自動負責釋放佔用的記憶體。    動態陣列長度可以隨程式的需要而重新指定大小。動態陣列由記憶體分配函式(malloc)從堆(heap

c++靜態函式動態函式的區別

在C語言中,由於沒有類的概念,所以沒有靜態成員一說,在c中static的作用主要有2個: 一是隱藏功能,對於static修飾的函式和全域性變數而言二是保持永續性功能,對於static修飾的區域性變數而言。並且,因為存放在靜態區,全域性和區域性的static修飾的變數,都預設

javaProxy(代理動態代理)

一、代理的概念  動態代理技術是整個java技術中最重要的一個技術,它是學習java框架的基礎,不會動態代理技術,那麼在學習Spring這些框架時是學不明白的。  動態代理技術就是用來產生一個物件的代理物件的。在開發中為什麼需要為一個物件產生代理物件呢?  舉一個現實生活中的

C# 在panel動態新增按鈕 分批刪除

參考文章:①http://www.cnblogs.com/yuzhihui/p/5749233.html②http://www.cnblogs.com/steed-zgf/archive/2012/04/03/2430819.html先來一張效果圖。畫布中的圖片是以 butt

Android 程式碼動態設定字型大小-TextView.SetTextSize()

關鍵程式碼  - setTextSize(TypedValue.COMPLEX_UNIT_PX,15); //22畫素  - setTextSize(TypedValue.COMPLEX_UNIT_SP,15); //22SP  - setTextSize(Type

QT選單Menu工具欄Toolbar各個Action的動態新增刪除

就像Swing裡面的Action一樣,Qt裡面也有一個類似的類,叫做QAction。顧名思義,QAction類儲存有關於這個動作,也就是action的資訊,比如它的文字描述、圖示、快捷鍵、回撥函式(也就是訊號槽),等等。神奇的是,QAction能夠根據新增的位置來改變自己的

Hadoop學習筆記—13.分散式叢集節點的動態新增下架

開篇:在本筆記系列的第一篇中,我們介紹瞭如何搭建偽分佈與分佈模式的Hadoop叢集。現在,我們來了解一下在一個Hadoop分散式叢集中,如何動態(不關機且正在執行的情況下)地新增一個Hadoop節點與下架一個Hadoop節點。 一、實驗環境結構   本次試驗,我們構建的叢集是一個主節點,三個從節點的結構,

Linux動態記憶體的分配回收(heap, buddy system, stab)

夥伴系統演算法   在實際應用中,經常需要分配一組連續的頁框,而頻繁地申請和釋放不同大小的連續頁框,必然導致在已分配頁框的記憶體塊中分散了許多小塊的 空閒頁框這樣,即使這些頁框是空閒的,其他需要分配連續頁框的應用也很難得到滿足   為了避免出現這種情況,Linux核心中引入了夥伴系統演算法(buddy

QTextEdit選中文字修改字型顏色,全部文字修改字型顏色

~~~~我的生活,我的點點滴滴!! 當然以下內容都可以通過設定樣式來達到目的,但是下面不使用這樣的方法 先來看張圖,理解此圖基本就能實現上面所要達到的目的了 Widget::Widget(QWidget *parent)     : QWidget(parent),

C++動態型別動態繫結、虛擬函式、執行時多型的實現

動態型別與靜態型別 靜態型別          是指不需要考慮表示式的執行期語義,僅分析程式文字而決定的表示式型別。靜態型別僅依賴於包含表示式的程式文字的形式,而在程式執行時不會改變。通俗的講,就是上下文無關,在編譯時就可以確定其型別。 動態型別         是指由一個

關於vcButton的顏色字型背景圖片的修改

void bb::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) {  CDC dc;    CRect rect;    dc.Attach(lpDrawItemStruct ->hDC);   // Get the Button DC to CDC rect=