NGUI 表情支持控件UIEmojiLabel,支持自适应多行多列
发表于2017-03-03
【原创】转摘请保留原文链接http://www.jianshu.com/p/a604b3f1fce3
NGUI出来已久,使用范围,场景越来越广。本身在高版本NGUI也支持表情,但存在一些缺陷,例如:支持文字和表情显示,但要求是必须文字和表情都需要在BMFont制作,对于文字量非常大的需求下,NGUI自带的文本混合表情,就显得力不从心。具体用过的同学,应该都知道的,为此下面就给大家介绍下NGUI 中的表情支持控件UIEmojiLabel,一起来看看吧。
NGUI控件扩展,支持自适应多行多列表情
思路:
1、输入文本表情,在文本处理的时候,处理表情字串,记录位置
2、根据位置信息,得到文本在渲染时候的顶点位置,存在this.geometry.verts中
3、为什么位置要*4? 因为每个字串有4个顶点数据
4、把站位的emSpace ,位置赋给UISprite(显示表情的控件)
5、所有的表情,通过表情管理器动态统一管理,不显示时,回收
1、UIEmojiLabel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | /* * Emoji Label @By Jumbo 2017/3/3 * */ using UnityEngine; using System.Collections; using System.Collections.Generic; using System; using System.Text; public class UIEmojiLabel : UILabel { private char emSpace = 'u2001' ; private List mEmojiList = new List(); public UIAtlas emAtlas = null ; protected override void OnStart() { base .OnStart(); //表情统一管理器,只初始化一次 UIEmojiWrapper.Instance.Init(emAtlas); } //自动转码 public override void OnFill (BetterList verts, BetterList uvs, BetterList cols) { base .OnFill(verts, uvs, cols); //Test 测试代码,正式使用,这里注释掉,外面改变Label 的值 string tx = "zhongz中国" + UIEmojiWrapper.Instance.GetConvertedString( "1f61c" ) + UIEmojiWrapper.Instance.GetConvertedString( "1f4fa" ) + UIEmojiWrapper.Instance.GetConvertedString( "1f63f" ) + UIEmojiWrapper.Instance.GetConvertedString( "1f607" ) + "国家回忆中心年内" + "sdfjsdlkj" + UIEmojiWrapper.Instance.GetConvertedString( "1f63b" ) + "国家" + UIEmojiWrapper.Instance.GetConvertedString( "1f467-1f3ff" ) + UIEmojiWrapper.Instance.GetConvertedString( "1f450-1f4ff" ); // + "好行。天使good bye"; text = tx; ///~测试代码 StartCoroutine(SetUITextThatHasEmoji(text)); } private struct PosStringTuple { public int pos; public string emoji; public PosStringTuple( int p, string s) { this .pos = p; this .emoji = s; } } public IEnumerator SetUITextThatHasEmoji( string inputString) { List emojiReplacements = new List(); StringBuilder sb = new StringBuilder(); //先回收 UIEmojiWrapper.Instance.PushEmoji( ref mEmojiList); int i = 0; while (i < inputString.Length) { string singleChar = inputString.Substring(i, 1); string doubleChar = "" ; string fourChar = "" ; if (i < (inputString.Length - 1)) { doubleChar = inputString.Substring(i, 2); } if (i < (inputString.Length - 3)) { fourChar = inputString.Substring(i, 4); } if (UIEmojiWrapper.Instance.HasEmoji(fourChar)) { // Check 64 bit emojis first sb.Append(emSpace); emojiReplacements.Add( new PosStringTuple(sb.Length - 1, fourChar)); i += 4; } else if (UIEmojiWrapper.Instance.HasEmoji(doubleChar)) { // Then check 32 bit emojis sb.Append(emSpace); emojiReplacements.Add( new PosStringTuple(sb.Length - 1, doubleChar)); i += 2; } else if (UIEmojiWrapper.Instance.HasEmoji(singleChar)) { // Finally check 16 bit emojis sb.Append(emSpace); emojiReplacements.Add( new PosStringTuple(sb.Length - 1, singleChar)); i++; } else { sb.Append(inputString[i]); i++; } } // Set text this .text = sb.ToString(); yield return null ; for ( int j = 0; j < emojiReplacements.Count; j++) { int emojiIndex = emojiReplacements[j].pos; //表情替换,计算位置,大小 GameObject go = UIEmojiWrapper.Instance.PopEmoji(); if (go != null ) { UISprite spt = go.GetComponent(); if (spt != null ) { string emoji = UIEmojiWrapper.Instance.GetEmoji(emojiReplacements[j].emoji); if (! string .IsNullOrEmpty(emoji)) { spt.name = emoji; spt.spriteName = emoji; } //mPrintedSize 父类权限私有,可以改为子类可范围的保护的权限,渲染的字体大小变化,来改变UISprite的大小 spt.width = this .mPrintedSize; spt.height = this .mPrintedSize; spt.transform.parent = this .transform; spt.transform.localScale = Vector3.one; spt.transform.localPosition = new Vector3( this .geometry.verts[emojiIndex * 4].x + spt.width / 2, this .geometry.verts[emojiIndex * 4].y + spt.height / 2); } mEmojiList.Add(go); } } } } |
2、UIEmojiLabelInspector 编辑器监视
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | /* * Emoji Label Inspector @By Jumbo 2017/3/3 * */ #if !UNITY_3_5 && !UNITY_FLASH #define DYNAMIC_FONT #endif using UnityEngine; using UnityEditor; /// /// Inspector class used to edit UILabels. /// [CanEditMultipleObjects] #if UNITY_3_5 [CustomEditor( typeof (UILabel))] #else [CustomEditor( typeof (UILabel), true )] #endif public class UIEmojiLabelInspector : UILabelInspector { /// /// Draw the label's properties. /// protected override bool ShouldDrawProperties () { bool isValid = base .ShouldDrawProperties(); EditorGUI.BeginDisabledGroup(!isValid); NGUIEditorTools.DrawProperty( "Atlas" , serializedObject, "emAtlas" ); //表情所在的图集Atlas EditorGUI.EndDisabledGroup(); return isValid; } } |
3、UIEmojiWrapper 表情统一管理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | /* * Emoji Wrapper @By Jumbo 2017/3/3 * */ using System; using System.Collections.Generic; using System.Text; using UnityEngine; class UIEmojiWrapper { private bool hasAtlas = false ; private UIAtlas mAtlas; private GameObject emojiPrefab; private Dictionary emojiName = new Dictionary(); private Queue freeSprite = new Queue(); private List usedSprite = new List(); private Vector3 OutOffScreen = new Vector3(10000f, 10000f, 10000f); private static UIEmojiWrapper sInstance; public static UIEmojiWrapper Instance { get { if (sInstance == null ) { sInstance = new UIEmojiWrapper(); } return sInstance; } } public void Init(UIAtlas atlas) { if (!hasAtlas) { if (atlas == null ) { LogUtil.LogError(WXLogTag.LogTag_UI, "[UIEmojiWrapper Atlas is null]" ); return ; } mAtlas = atlas; //预分配 for ( int i = 0, cnt = mAtlas.spriteList.Count; i < cnt; i++) { emojiName.Add(GetConvertedString(mAtlas.spriteList[i].name), mAtlas.spriteList[i].name); } emojiPrefab = new GameObject(); UISprite spt = emojiPrefab.AddComponent(); if (spt != null ) { spt.transform.localPosition = OutOffScreen; spt.name = "emojiPrefab" ; } hasAtlas = true ; } } public string GetConvertedString( string inputString) { string [] converted = inputString.Split( '-' ); for ( int j = 0; j < converted.Length; j++) { converted[j] = char .ConvertFromUtf32(Convert.ToInt32(converted[j], 16)); } return string .Join( string .Empty, converted); } public string GetEmoji( string encode) { string em; if (emojiName.TryGetValue(encode, out em)) { return em; } return null ; } public bool HasEmoji( string key) { return emojiName.ContainsKey(key); } public void OnPostFill (UIWidget widget, int bufferOffset, BetterList verts, BetterList uvs, BetterList cols) { if (widget != null ) { if (!widget.isVisible) { UISprite spt = widget as UISprite; if (spt != null ) { PushEmoji(spt.gameObject); } } } } public GameObject PopEmoji() { if (freeSprite.Count <= 0) { if (emojiPrefab == null ) return null ; GameObject tran = GameObject.Instantiate(emojiPrefab) as GameObject; UISprite spt = tran.GetComponent(); if (spt != null ) { spt.atlas = mAtlas; spt.hideIfOffScreen = true ; spt.onPostFill += OnPostFill; } freeSprite.Enqueue(tran); } GameObject sptRet = freeSprite.Dequeue(); if (sptRet != null ) { usedSprite.Add(sptRet); } return sptRet; } public void PushEmoji(GameObject spt) { spt.transform.localPosition = OutOffScreen; spt.transform.parent = null ; freeSprite.Enqueue(spt); } public void PushEmoji ( ref List list) { for ( int i = 0, cnt = list.Count; i < cnt; i++) { PushEmoji(list[i]); } list.Clear(); } } |
有更好的方案,不吝赐教~~
【原创】转摘请保留原文链接http://www.jianshu.com/p/a604b3f1fce3
Demo效果:

[文本表情混排效果]