在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修飾的變數,都預設
java中Proxy(代理與動態代理)
一、代理的概念 動態代理技術是整個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++中的動態型別與動態繫結、虛擬函式、執行時多型的實現
動態型別與靜態型別 靜態型別 是指不需要考慮表示式的執行期語義,僅分析程式文字而決定的表示式型別。靜態型別僅依賴於包含表示式的程式文字的形式,而在程式執行時不會改變。通俗的講,就是上下文無關,在編譯時就可以確定其型別。 動態型別 是指由一個
關於vc中Button的顏色字型與背景圖片的修改
void bb::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CDC dc; CRect rect; dc.Attach(lpDrawItemStruct ->hDC); // Get the Button DC to CDC rect=