NGUI 表情支持控件UIEmojiLabel,支持自适应多行多列

发表于2017-03-03
评论0 1.8k浏览
【原创】转摘请保留原文链接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效果:
[文本表情混排效果]

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引