Android 開發,你遇上 Emoji 頭疼嗎?

承香墨影發表於2017-12-29

188

在 Android 中,如果需要使用的到 Emoji 表情,你會發現在某些裝置上,有一些 Emoji 表情會被以豆腐塊 “☐” 的形式顯示,這是因為當前裝置並不支援這個 Emoji 表情。

而在 Android Support 中,新增加了一個 EmojiCompat 來專門解決這個問題,EmojiCompat 對 Android 4.4(Api Level 19)以及之後的系統,進行 Emoji 的擴充套件支援!

接下來我們就來了解使用 EmojiCompat 的所有細節!

一、什麼是 Emoji?

既然要用到 Emoji ,我們先來了解一下什麼是 Emoji。

Emoji 是可以被插入文字中的圖形符號。它是一個日本語,e 表示"繪",moji 表示 "文字" ,連在一起就是 “繪文字”。

Emoji 最早是上個世紀 90 年代的時候,又日本電信商率先支援,就是為了在簡訊中,插入表情,來增強簡訊的體驗。2007 年,Apple 在 iPhone 中支援了 Emoji,才讓它在全球範圍內流行起來。

iphone-emoji

早期的時候,Emoji 的實現是將一些特殊的符號組合替換成圖片表情,例如 :) 替換成 ? ,這樣的解決方案會導致很難將 Emoji 的表情標準化,而且表達的範圍也有限。

2010年開始,Unicode 開始為 Emoji 分配碼點,也就是說,在那之後的 Emoji 符號,就是一個字型,它會被渲染為圖片顯示。

Emoji 由於其表達情緒的特點,被廣受歡迎。Emoji 的國際標準在 2015 年出臺,現在已經是 5.0 版本了,而在 2018 年,將釋出 Emoji 6.0(之後會重新命名為 Emoji 11,其實就是 Emoji 5.0 的升級版) 版本的標準。

截止到現在,Emoji 5.0 中,被列入 Unicode 的已經有 2623 個了。

具體細節可以在這個網站中查詢到:

http://www.unicode.org/emoji/charts/full-emoji-list.html

到這裡大家應該清楚,Emoji 在標準化後,其實就是一個字型,它被 Unicode 分配了固定的碼點,一般我們就用標準的 Unicode 來標識一個唯一的 Emoji。

雖然 Emoji 已經被標準化了,但是不同平臺因為使用的字型不同,導致同樣的 Unicode 代表的 Emoji,被渲染顯示出不同的效果。

emoji-transfrom

例如這裡舉的例子,標準的 Emoji U+1F601,在 Apple 和 Android 中,雖然同樣表示一個笑臉,但是渲染的效果是不一樣的,這一點我們需要了解到。

二、Emoji 在 Android 中的現狀

2.1 如何使用 Emoji?

一個標準的 Emoji ,其實是有多種表示方法的,舉個例子,先看看前面說的笑臉 U+1F601

xiaolian-emoji

Code、UTF-8、Surrogates 這些形式,都可以表示這個笑臉的 Emoji。通常這個 Emoji 表情是來自使用者輸入的資料或者服務端傳遞過來的資料,雖然這些形式都可以表示這個 Emoji,但是不同的格式就需要不同的形式來解析。

正常來說,我們推薦使用 Surrogates 傳遞 Emoji,例如:\uD83D\uDE01,它本身就是一個 Unicode 的編碼,是通用的,可以在 TextView 中直接使用就可以顯示。

xiaolian-unicode

可是,假如我們得到的並不是 Surrogates ,而是 Code ,例如 1F601,這樣我們就需要進行額外的處理了。其實也很簡單,經過幾步轉換就可以做到。

String(Character.toChars(Integer.parseInt("1F601", 16)))
複製程式碼

例子中使用的是 Kotlin 程式碼,不過應該不影響閱讀。

將生成的 String 物件,傳遞給 TextView,如果是當前裝置支援的 Emoji,就可以正常顯示了。

2.2 Emoji 顯示不出來情況

上一小節介紹的方式,其實我們是沒有做任何特殊處理的,完全是以來裝置自己的字型庫來進行 Emoji 渲染的。這就會導致有一些 Emoji 在某些裝置上顯示不出來的情況。

使用這種方案,我用我手邊的裝置執行之後,來看看顯示的效果。

emoji-normal-test

很清晰的看到,這裡有一些 Emoji 無法顯示,會被顯示成一個豆腐塊 “☐” ,而這並不是我們想要的。

接下來我們來看看使用 EmojiCompat 如何來處理它。

三、使用 EmojiCompat

3.1 什麼是 EmojiCompat

根據官方文件描述,EmojiCompat 支援庫主要是為了讓 Android 裝置,達到最新的 Emoji 符號的顯示效果,它可以防止應用中,出現以豆腐塊 “☐” 的形式來顯示 Emoji,雖然它僅僅只是因為你當前的裝置沒有這個字型而已。通過 EmojiCompat ,你的裝置無需等待 Android 系統更新,就可以獲得最新的 Emoji 表情顯示效果。

emoji-comparison

3.2 如何使用 EmojiCompat

EmojiCompat 支援庫,最低支援到 Android 4.4(Api Level 19) 的系統裝置。

EmojiCompat 提供兩種字型的支援方式,它們分別是:

  1. 可下載的字型配置。
  2. 本地捆綁的字型配置。

這兩種使用方式,除了引用的庫不同之外,最根本的原因在於,可下載的字型的方式,會在首次啟動的時候檢查本地是否有該字型,沒有的話會從網上下載最新的 Emoji 字型;而本地捆綁的方式,會在 App 打包的過程中,植入一個最新的 Emoji 字型檔案,然後遇到不能支援的 Emoji,就會從這個字型檔案中,載入資源並且渲染。

目前官方使用的是 NotoColorEmojiCompat.ttf 字型檔案,後面會詳細講解到。

我想你應該發現了,本地捆綁的方式會嵌入一個字型檔案,無形中增大了 Apk 安裝包的體積,但是可下載字型的方式,又完全依賴 Google 服務,所以在國內基本上是處於殘廢狀態,在這個大環境下,我們這裡只能選擇本地捆綁的方式來使用 EmojiCompat。

無論使用哪種方式配置字型,對於 EmojiCompat 而言,其實是不關心的,它只需要判斷當前裝置是否支援這個 Emoji,支援就使用系統內建的,不支援的話,就使用 EmojiSpans 來替換 CharSequence,來達到替換渲染的效果。

EmojiCompat 的執行原理如下圖所示。

emoji-support

3.3 本地捆綁的字型配置方式

既然可下載的 Emoji 字型,需要配合 Google 服務,那這裡就不再過多介紹了。

本文主要講解如何使用本地捆綁的方式,使用 EmojiCompat。

第一步,需要新增 Gradle 依賴。

dependencies {
    ...
    compile "com.android.support:support-emoji-bundled:27.0.2"
}
複製程式碼

第二步,初始化 EmojiCompat。

初始化 EmojiCompat ,需要兩個步驟。

  1. 首先需要生成一個 BundledEmojiCompatConfig 物件,它的構造方法接收一個 Context。
  2. 再呼叫 EmojiCompat.init() 方法,將前面生成的 config 傳遞給它進行初始化。

emoji-init

這個過程越早越好,因為初始化是耗時的,它會去載入打包的時候,嵌入的 Emoji 字型檔案,所以大概需要消耗 150ms 的時間,並且佔用大概 200kb 的記憶體。

第三步,使用 EmojiCompat。

初始化完成之後,就剩下如何使用它了。

EmojiCompat 的處理邏輯,前面已經使用圖片描述清楚了。它會載入一個 Emoji 字型,然後判斷當前裝置是否支援需要顯示的 Emoji,如果不支援,則使用 EmojiSpans 替換它,最終將處理過的 CharSequence 設定到 TextView 上。

而這個過程,EmojiCompat 提供了非常簡單的方法,process()

compat-process

從它的簽名可以看出,它接受一個 CharSequence 並處理它,然後返回一個 CharSequence。

舉個例子:這裡轉換一個笑臉的表情。

EmojiCompat.get().process("笑臉: \uD83D\uDE01")
複製程式碼

process() 需要接受一個 Unicode 的字元,所以如果得到的資料是前面提到的 Emoji Code 的話,就需要一步單獨的轉換。process() 內部已經幫我們完成了轉換,這些細節都無需我們關心,我們只需要將它返回的 CharSequence 設定給 TextView 就可以了。

3.4 Emoji AppCompat Widgets

在實際專案中,如果每次都需要通過 EmojiCompat.get().process() 對字串進行處理,其實也挺麻煩的。為此 Google 還為開發者提供了對應控制元件支援。

如果需要使用它,就需要引入新的依賴庫。

dependencies {
      compile "com.android.support:support-emoji-appcompat:27.0.2"
}
複製程式碼

引入之後,就可以直接在 XML 中使用 EmojiAppCompat 提供的控制元件。

emoji-widget

使用 support-emoji-appcompat 只是節省了我們 process() 的步驟,但是依然需要 init()

3.5 自定義控制元件支援 Emoji

你可以一直使用 progress() 或者使用 EmojiAppCompatXxx 控制元件,但是如果你想要自定義一個控制元件來顯示 Emoji,就需要使用 EmojiCompat 提供的另外兩個幫助類。

  • EmojiTextViewHelper
  • EmojiEditViewHelper

這兩個使用起來非常簡單,一個用於處理純展示的控制元件,一個用於處理有輸入的狀態的控制元件,非常的簡潔明瞭。

哪怕不記得了,看看 EmojiAppCompatTextView 和 EmojiAppCompatEditView 中的實現方式就可以了。

AppCompatTextView

這裡拿 EmojiAppCompatTextView 舉例子,只需要在幾個關鍵的位置上,使用 EmojiTextViewHelper 的對應方法即可。

3.6 EmojiCompat面臨的問題

整體來說 EmojiCompat 還是很好用的,無論使用哪種方式載入它,實際上我們都不需要做過多的干預。

這裡參考官方文件,列舉最常見的幾個問題。

1、下載字型的下載策略是怎麼樣的?

Emoji 字型在第一次使用的時候,會檢測是否存在於當前裝置,如果不存在則在子執行緒中進行下載。

2、初始化需要多長時間?

當本地已經有字型之後,初始化 EmojiCompat 大約需要 150 毫秒。

3、EmojiCompat 支援庫,會使用多少記憶體?

目前,Emoji 字型被完全載入之後,會使用大約 200kb 的記憶體。

4、在 Android 4.4 以下的裝置上,使用 EmojiAppCompatXxx 控制元件會發生什麼情況?

EmojiCompat 內部已經做了相容處理,在低版本上就和使用普通的 AppCompatXxx 控制元件一樣。

5、本地捆綁的 Emoji 字型檔案,大約有多大?

本地捆綁的 Emoji 字型檔案 NotoColorEmojiCompat.ttf,會在打包的時候嵌入到 assets 目錄下,現在實際情況來看大小有 7.4MB,這會直接造成 Apk 的增大。

更多的細節,還是建議大家閱讀官方文件。

https://developer.android.google.cn/guide/topics/ui/look-and-feel/emoji-compat.html

四、EmojiCompat 的缺陷?

在實際使用 EmojiCompat 的過程中,還遇到了一個不能算缺陷的缺陷。

我們來回憶一下之前提到過的,EmojiCompat 的處理機制。

emoji-support

它只有在當前裝置遇到不被支援的 Emoji 的時候,才會從 Support Font 中載入字型,如果有,它會使用 System Font 。

這也不能怪 EmojiCompat 的設計者,它的出發點,是為了解決 Emoji 在某些裝置中,顯示豆腐塊 “☐” 的問題,而不關心它到底是不是顯示最新的 Emoji,是在解決有無的問題。

這就很尷尬了,其實有時候 Android 裝置內建支援的字型,顯示的效果並不好,我們先來看看使用 EmojiCompat 前後的對比效果。

emoji-duibi

左邊是沒有使用 EmojiCompat 的效果,而右邊是使用過的效果。

很清晰的可以看到 EmojiCompat 幫我補齊了我當前裝置部支援的那些 Emoji 表情,但是並沒有將 Android 的果凍表情替換為標準的 Emoji 表情。

那麼,如果我們想要讓它顯示最新的 Emoji ,我們需要這麼辦呢?

前面提到,自從 Emoji 開始被標準化之後,其實就是一個字型,並且 EmojiCompat 也是幫我們捆綁嵌入了一個字型包在 assets 目錄下,那我們只需要讓我們顯示的 TextView 載入這個 Emoji 字型,就可以解決這個問題。

有了思路,我們就來試試這個解決方案是否可行。

emojiUtils

到此,我們就可以通過呼叫 loadEmoji() 方法,讓 TextView 顯示 Emoji ,來看看對比的效果。

emoji-duibi3

從左到右,分別是:預設 Emoji、EmojiCompat、Emoji Font 的顯示效果。

密密麻麻的表情有點多,密集恐懼症請放過我,?!

當然,這裡只是提供一個解決方案,採用此方案的情況下,基本上所有 4.4 以上的機型,都可以顯示最新的 Emoji,如果對 Emoji 的顯示效果有要求,這也不失為一個解決方案。

五、總結

到此,就是我所瞭解的 Android 下的 Emoji。

看完本文你有什麼收穫?或者你有什麼更好的關於 Emoji 的建議,歡迎在留言討論!

今天在承香墨影公眾號的後臺,回覆『成長』。我會送你一些我整理的學習資料,包含:Android反編譯、演算法、設計模式、虛擬機器、Linux、Kotlin、Python、爬蟲、Web專案原始碼。

推薦閱讀:

Android 開發,你遇上 Emoji 頭疼嗎?

相關文章