前言

众所周知,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,或者运行一下游戏就好了。

上两张效果图:

最后放上Github地址
https://github.com/OYYMING/EmojiLabel


PS:我使用的NGUI版本是3.8.2,其中比较重要的修改是UILabel,NGUIText,UILabelInspector这三个文件。

在NGUI的UILabel中使用动态字体与表情相关推荐

  1. Unity3D中的动态字体和静态字体

    Unity3D中支持动态字体和静态字体两种格式字体,动态字体即使用TTF格式字体库,静态字体则需要自己打包字体图集.动态字体和静态字体区别在于,动态字体如果出现字体库中不存在的字体,会使用系统字体,而 ...

  2. cad2016中选择全图字体怎么操作_PPT中制作动态字体,这样操作也很简单,独树一帜...

    今天和大家分享一个PPT技巧怎样使用PPT制作不一样的动态文字,不仅操作简单使用起来也很便利呢?在工作,生活中都可以使用,下面一起来看看绘制技巧吧! 操作方法 使用工具:电脑/PPT 1.新建一个PP ...

  3. iOS中的所有字体和UILabel

    2019独角兽企业重金招聘Python工程师标准>>> 获取iOS中所有的字体 - (void)viewDidLoad {[super viewDidLoad];//获取iOS中的所 ...

  4. NGUI动态字体教程

    注:此教程仅是用于Unity 4+ 第1 步:导入NGUI 第2 步:导入NGUI_277c_DynamicFonts.unitypackage 如果使用的是ngui2.3.1 或更高的话替换UIFo ...

  5. Xcode中的隐藏宝藏:模拟器里如何快速预览动态字体显示效果

    功能需求 在我们App适配动态字体后,如何快速预览不同动态字体的显示效果呢? 按照如上的操作在模拟器中会非常麻烦,那么有没有更简单的方法呢? 答案是肯定的! 从Xcode 10 开始,Apple为我们 ...

  6. flutter取消动态字体大小

    如果接触过原生开发的朋友可能知道 最初几年官方推荐使用 sp 作为字体大小的单位,但是事实上经过长时间实践的情况下,大家都开始逐渐使用 dp 作为单位 这样使用者调整系统字体大小,app 中的文字大小 ...

  7. unity 动态字体 和 静态字体

    Unity3D中的动态字体和静态字体_Wei_Yuan_2012的专栏-CSDN博客_unity 动态字体和静态字体 静态字体(自定义字体)用来做美术字

  8. 名编辑电子杂志大师教程 | 制作产品电子目录时如何添加动态字体?

    用名编辑电子杂志大师能够在您的产品目录中添加动态字体,可以在产品目录的页面添加公司信息,产品介绍等文字信息. 名编辑软件中,提供了6种文本方式,数十种文字展示效果,用它制作产品电子目录,设计出来的效果 ...

  9. NGUI聊天信息中插入动态表情

    聊天系统中难免需要加入动态表情来增加表现力,而使用NGUI中自带的表情系统有些局限:一是只能使用静态表情,二是需要制作字体图集,不能使用动态图集.所以需要另外寻找方法实现聊天文字中插入动态表情的效果. ...

最新文章

  1. C++模式学习------工厂模式
  2. loaction.reload(false)和location.reload(true) js发起请求
  3. 【Matlab 控制】利用 XY Graph 画图
  4. Nginx(零):背景
  5. 信息学奥赛一本通 1173:阶乘和 | OpenJudge NOI 1.6 15 | 洛谷 P1009 [NOIP1998 普及组] 阶乘之和
  6. 解决javax.servlet.jsp.JspException cannot be resolved to a type
  7. openglpython3d重构_python+opengl显示三维模型小程序
  8. 计算机多媒体技术怎么考一建,一建考试视频
  9. 图像算法一:【图像点运算】灰度直方图、伽马变换、灰度阈值变换、分段线性变换、直方图均衡化
  10. 小马哥linux网络培训,linux基础入门
  11. 2006-01-23,科比-布莱恩特得了81分
  12. 21种低成本的方式,帮助企业获得关注
  13. 仿淘宝购物车demo---增加和减少商品数量
  14. ubuntu终端字体大小和窗口大小设置
  15. 【转载】API:简单实现多人聊天室
  16. 调试MCP2515驱动时遇到的问题 CAN控制器和CAN收发器之间的TX和RX引脚接反,导致无法正常收发数据
  17. 关系型数据库保证数据完整性和一致性的方法
  18. 制作PDF文件全攻略
  19. 机器人布里茨说什么_英雄联盟布里茨台词有哪些
  20. Python+PySide2:Label 插入动态GIF图片

热门文章

  1. Turf.js(地理空间GIS分析的js库),处理地图相关算法
  2. 俞敏洪在同济大学的演讲词
  3. 垃圾分类与AI的反碎片之旅
  4. 聚观早报 | 多款热门游戏停服一天;比亚迪下月在日本开售
  5. 卸载wdcp面板命令
  6. 第17章 有关事务的楔子
  7. vue项目引入本地图片不显示的解决方法
  8. BCryptPasswordEncoder加密和匹配的原理 和 springsecurity 的 rememberme原理
  9. Java基础语法(九)——String类
  10. 快来看一看String类内部是如何实现的