Android emoji

网络聊天中,对于不善表达的人来说, “表情” 的诞生算是神赐之物;用上表情,似乎就可以扮演一个情绪切换自如的年轻人

我们现在常用的无论是Emoji,还是图文表情包,不难发现,很多表情追求仪式感,重在会意与实时体验,它们大多都是对面部表情和肢体动作的再现,以一种“仿真”的身体在场

微信表情里的玫瑰花,带着长长的花茎,发出去或者接收,都像有 “握住” 的代入感;拥抱像是卡通形象,一种安慰情境下的可操作表达,既可以看作是发送者的双臂,也可以是接收者的双臂……这些表情弥补了网络传播中无法感知情绪变化的缺陷

安卓emoji扩展(解读网络聊天中的)(1)

解读 emoji

"emoji" 中176个符号,有开心,有吐舌头,还有亲吻,还有眨眼,还有闭嘴等等;在网络和手机中都能够看到这些符号,在2014年的时候已经统一版本,并且统一所有码标准,紧接着又推出了250个新的emoji表情,这让大家在网络上应用更加的舒适

在2014年8月的时候有一本牛津词典,将这些表情符号编辑到其中,这也就说明这些表情符号成为正式的词汇,将在以后的生活以及工作中,得到广泛的应用,在苹果公司把这些表情收藏之后,网络上有更多的用户使用这些表情包,并且现在已经席卷全球;这些表情符比之前提到的囧更加广泛,很多人都在使用

emoji的实用性

在这些表情中,非常符合现实的生活; 例如说在不开心的时候可以打一个哭脸的表情,就像平时大家的心情一样,例如笑脸,代表着友好也代表着礼貌,但是在表情中的笑脸非常的特别,稍稍的有些向下看,给人一种意味深长的感觉,所以特别的有意思

在和女朋友或者是老婆聊天的时候可以打一个心; 证明此时此刻正在想念着对方,但是有网友说为什么在这些表情里面没有比心,比心是现在比较流行的一个手势,所以很多年轻的网友,在网络平台上留言,希望在以后能看到这个手势出现,相信不久的将来就会更新的

在很多的表情里面,最受人欢迎的就是红桃心; 这一颗红红的爱心代表着喜欢,代表着期待,也有很多人用这个表情来表示自己的感谢心情。这个表情给人的感觉是非常礼貌,情侣之间使用代表着想念,代表着浪漫。朋友之间使用代表着友情更加贴近

Emoji 表情介绍

下面大概介绍一下关于 Emoji 表情:

Emoji 是可以被插入文字中的图形符号,它是一个日本语; e 表示 "绘", moji 表示 "文字" , 连在一起就是 “绘文字”, 它最早是用于我们发短信来增强用户的一个体验,2007 年,Apple 在 iPhone 中支持了 Emoji,才让它在全球范围内流行起来

● 在 2010 年以前,Emoji 的实现是将一些特殊的符号组合替换成图片表情,例如 :) 替换成 ,这样的解决方案导致 Emoji 表情很难标准化,而且表达范围有限

● 从 2010 年开始,Unicode 开始为 Emoji 分配固定的码点,也就是说,在这之后,每一个 Unicode 字符对应一个字体,它会被渲染为图片显示

● Emoji 表情由于其表达情绪的特点,被广受欢迎。Emoji 表情的国际标准在 2015 年出台,到目前为止已经是 V13.1 版本的标准了

需求

对 Emoji 表情有了一个大概的了解之后,下面讲下产品给我提的一个需求,大概就是:从中,筛选一些常用的 Emoji 表情,然后根据 UI 设计稿,实现表情包功能即可

安卓emoji扩展(解读网络聊天中的)(2)

从上图我们可以看到:

每个表情的 Unicode 字符

● 这里解释一下 Unicode:Unicode 就是统一的字符编码标准,当需要表示一个 Unicode 字符时,通常会用 U 然后紧接着一个十六进制的数字来表示,如上图所列举的这些

● 每个 Unicode 字符对应的 Emoji 表情在各个平台展示的样式都不太一样,因为 Unicode 只是规定了 Emoji 的码点和含义,并没有规定它的样式,每个平台都有自己的 Emoji 实现

Emoji 表情实践

有了思路,就开始撸起柚子干,把筛选出的表情 code 放到一个集合中,然后通过 ViewPager Fragment RecyclerView 等一系列控件的配合,实现了 UI 需要的效果

如下图:

安卓emoji扩展(解读网络聊天中的)(3)

从上图可以发现一个问题:有些 Emoji 表情显示出来像一个信封

原因是当前设备不支持,上面我讲到每一个 Unicode 字符对应一个字体,它会被渲染为图片显示,但是如果当前系统不支持这种字体,那么就会显示出一个信封,而且随着 Android 版本越来越低,这种情况越来越多,这种效果肯定是不行的

知道了出现的问题和原因,我们就要去想解决方法,这个时候 EmojiCompat 就来了

EmojiCompat 介绍什么是 EmojiCompat ?

EmojiCompat 是 Google 官方给我们提供的一个 Emoji 表情兼容库; 最低支持到 Android 4.4(Api Level 19) 的系统设备,它可以防止应用中,出现以信封的形式来显示 Emoji,虽然它仅仅只是因为你当前的设备没有这个字体而已;通过 EmojiCompat ,你的设备无需等待 Android 系统更新,就可以获得最新的 Emoji 表情显示效果

EmojiCompat 的运行原理如下图所示:

安卓emoji扩展(解读网络聊天中的)(4)

从上图我们可以知道:EmojiCompat 会判断当前设备是否支持这个 Emoji,如果支持则还是使用系统内置的字体加载,如果不支持,则使用 EmojiSpan 来进行替换,从而达到替换渲染的效果

如何使用 EmojiCompat ?

要使用 EmojiCompat ,我们需要先对其进行初始化,如下:

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n80" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">EmojiCompat.init(config); //EmojiCompat 的 init 方法 public static EmojiCompat init(@NonNull final Config config) { if (sInstance == null) { synchronized (sInstanceLock) { if (sInstance == null) { sInstance = new EmojiCompat(config); } } } return sInstance; }</pre>

上述代码可以看到, EmojiCompat 是一个单例对象,初始化方法就是传入了一个 config 即配置,因此构建配置是 EmojiCompat 初始化能否成功的重点所在,Google 给我们提供了两种配置

他们分别是:

● 可下载的字体配置

● 本地捆绑的字体配置

根据 Google 官方介绍:

● 可下载的字体配置 原理:可下载的字体的方式会在首次启动 app 的时候检查本地是否有该字体,没有的话会从网上下载最新的 Emoji 字体,然后遇到不支持的 Emoji,就会从这个字体文件中,加载资源并且渲染

缺点: 可下载字体的方式,完全依赖 GMS 服务,在没有 GMS 服务的手机上并不可用

● 本地捆绑的字体配置 原理:本地捆绑的方式会在 App 打包的过程中,植入一个最新的 Emoji 字体文件,然后遇到不支持的 Emoji,就会从这个字体文件中,加载资源并且渲染

缺点: 本地捆绑的方式会嵌入一个约 9M 的字体文件,无形中增大了 Apk 安装包的体积

EmojiCompat 实践

因为本地捆绑字体配置的方式会使我们的 app 包体积增大 9M ,这是完全不能接受的; 而且我们的 app 主要是面向国外的用户,国外用户手机一般都有 GMS 服务,因此我选用了可下载字体配置来完成 EmojiCompat 的初始化

初始化成功后,我们就可以使用 EmojiCompat 提供的功能了,之前我们是通过如下方式进行表情包加载的:

<pre mdtype="fences" cid="n102" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">//将当前 code 转换为 16 进制数 int hex = Integer.parseInt("1F600", 16); //将当前 16 进制数转换成字符数组 char[] chars = Character.toChars(hex); //将当前字符数组转换成 TextView 可加载的 String 字符串 String mEmojiString = new String(chars);</pre>

现在只需要对当前 mEmojiString 通过 EmojiCompat 处理一下即可,如下:

<pre mdtype="fences" cid="n108" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">//判断当前 EmojiCompat 是否初始化成功 public static boolean isEmojiCompatInit(){ return EmojiCompat.get().getLoadState() == EmojiCompat.LOAD_STATE_SUCCEEDED; } //获取可兼容的 emoji 字符串 public static CharSequence getCompatEmojiString(String code) { //将当前 code 转换为 16 进制数 int hex = Integer.parseInt(code, 16); //将当前 16 进制数转换成字符数组 char[] chars = Character.toChars(hex); //将当前字符数组转换成 TextView 可加载的 String 字符串 String mEmojiString = new String(chars); //判断当前系统是否大于等于 19,并且 EmojiCompat 初始化成功 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && isEmojiCompatInit()){ return EmojiCompat.get().process(mEmojiString); } return mEmojiString; }</pre>

上述代码我们使用 EmojiCompat 的 process 方法对之前的 emoji 字符串做了兼容处理,现在显示出来的表情就不会有啥问题了,这个库使用起来还是很简单的

尾述

私信发送 “底层源码” 即可免费获取 完整代码 以及 更多学习笔记 面试视频

技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面

Android 架构师之路还很漫长,与君共勉

PS:有问题欢迎指正,可以在评论区留下你的建议和感受; 欢迎大家点赞评论,觉得内容可以的话,可以转发分享一下

,