Fork me on GitHub

 

Emoji兼容性

我们经常会遇到这样的问题: 给朋友发的emoji表情, 在自己手机上展示是正常的, 但是到朋友手机上, 却没有展示出来, 或者展示出来了, 但是也跟自己手机上展示的不一样. 所以, 这是什么原因呢?

要避免这种情况, 就需要使用Android Emoji的兼容包了. 

 

Emoji兼容包目的在于保持Android设备拥有最新的Emoji. 它防止应用使用☐展示丢失的Emoji字符, 而☐意味着设备没有字体支持相应的文本. 通过使用EmojiCompat支持包, 应用的用户不必等候Android系统更新就可以获得最新的Emoji.

 

EmojiCompat工作原理

 

EmojiCompat支持包向运行Android 4.4(API 19)+的设备提供类以实现向后兼容的Emoji支持. 你可以配置EmojiCompat使用绑定的或者可下载的字体.

EmojiComat识别指定的CharSequence, 如果必要的话, 会使用EmojiSpans代替它们, 并最终渲染成emoji符号.

 

可下载字体配置

 

可下载字体配置使用Downloadable Fonts支持包特性来下载emoji字体. 该支持包也更新必要的emoji元数据, EmojiCompat支持包需要与最新的Unicode版本保持一致.

 

添加支持包依赖

 

要使用EmojiCompat支持包, 必要要修改开发环境的应用工程路径依赖.
要在应用中添加支持包, 需要:

  1. 打开应用build.gradle文件.
  2. 将依赖包添加到dependencies区域

 1 dependencies { 2 ... 3 compile "com.android.support:support-emoji:27.1.1" 4 } 

 

初始化可下载字体配置

 

你需要初始化EmojiCompat来下载元数据和字样. 因为初始化会花费一些时间, 所以初始化进程要运行在后台线程.

要初始化EmojiCompat可下载字体配置, 执行以下步骤:

  • 创建FontRequest类实例并提供字体提供者权限, 字体提供者包, 字体查询以及认证的hash集列表. 
  • 创建FontRequestEmojiCompatConfig实例并提供Context实例和FontRequest.
  • 调用init()方法初始化EmojiCompat并传递FontRequestEmojiConfig实例
 1 public class MyActivity extends Activity {
 2     @Override
 3     public void onCreate() {
 4         super.onCreate();
 5         FontRequest fontRequest = new FontRequest(
 6                "com.example.fontprovider",
 7                "com.example",
 8                "emoji compat Font Query", CERTIFICATES);
 9         EmojiCompat.Config config = new FontRequestEmojiCompatConfig(this, fontRequest);
10         EmojiCompat.init(config);
11         ...
12     }
13 }

 

  • 在布局文件中使用EmojiCompat控件.

 

 1 <android.support.text.emoji.widget.EmojiTextView
 2    android:layout_width="wrap_content"
 3    android:layout_height="wrap_content"/>
 4 
 5 <android.support.text.emoji.widget.EmojiEditText
 6    android:layout_width="wrap_content"
 7    android:layout_height="wrap_content"/>
 8 
 9 <android.support.text.emoji.widget.EmojiButton
10    android:layout_width="wrap_content"
11    android:layout_height="wrap_content"/>

 

包构件

 

构件: EmojiEditText, EmojiTextView, EmojiButton. 这些构件是在TextView, EditText和Button上实现EmojiCompat的默认控件实现.

  • EmojiCompat: 支持包的主要公共接口. 它执行了所有的外部调用, 并与系统的其它部分协调.
  • EmojiCompat.Config: 配置要创建的单例.
  • EmojiSpan: ReplacementSpan子类, 取代字符(序列)并渲染字符.
  • EmojiCompat Font: EmojiCompat使用字体展示emoji. 字体是Android Emoji Font的修改版本. 字体接如下规则修改:
  1. 要提供向后兼容性来渲染emoji, 所有的emoji字符用单个Unicode码点表示, 这个码点位于Unicode Supplement Private Use Area-A, 从U+F001开始的
  2. 额外的emoji元数据以二进制格式插入字体, 并在运行时被EmojiCompat解析. 这些数据嵌套在字体的meta表中, 并含有私有标签Emji.

 

配置选项

 

你能够使用EmojiCompat实例修改EmojiCompat行为. 你可能使用源于基数的如下方法设置配置:

  • setReplaceAll(): 决定了EmojiCompat是否应该取代它用EmojiSpans找到的所有emoji. 默认情况下, EmojiCompat尽已所能理解系统是否能够渲染emoji, 但并不取代它们. 设置成true的时候, EmojiCompat会取代它用EmojiSpans找到的所有emoji.
  • setEmojiSpanIndicatorEnabled(): 指明EmojiCompat是否用EmojiSpan取代emoji. 设置成true的时候, EmojiCompat为EmojiSpan绘制背景. 但这个方法主要用于debug.
  • setEmojiSpanIndicatorColor(): 设置指明EmojiSpan的颜色. 默认值是GREEN.
  • registerInitCallback(): 告知应用EmojiCompat初始化的状态.
1 EmojiCompat.Config config = new FontRequestEmojiCompatConfig(...)
2        .setReplaceAll(true)
3        .setEmojiSpanIndicatorEnabled(true)
4        .setEmojiSpanIndicatorColor(Color.GREEN)
5        .registerInitCallback(new InitCallback() {...})

 

添加初始化监听器

 

EmojiCompat类提供了registerInitCallback()和unregisterInitCallback()方法注册初始化回调. 要使用这些方法, 先创建EmojiCompat.InitCallback类, 调用这些方法, 然后传入EmojiCompat.InitCallback实例. 在EmojiCompat支持包初始化成功的时候, EmojiCompat类调用了onInitialized()方法. 如果库初始化失败了, EmojiCompat类调用onFailed()方法.

要想在任何时刻查看初始化状态, 调用getLoadState()方法. 它返回下列值之一: LOAD_STATE_LOADING, LOAD_STATE_SUCCEED或者LOAD_STATE_FAILED.

 

用AppCompat控件使用EmojiCompat

 

如果你在使用AppCompat控件, 那么你可能使用继承自AppCompat控件的EmojiCompat控件.
1, 添加如下依赖包.
 1 dependencies { 2 compile "com.android.support:support-emoji-appcompat:$version" 3 } 
2, 在布局文件中使用EmojiCompat AppCompat Widget.

 1 <android.support.text.emoji.widget.EmojiAppCompatTextView
 2    android:layout_width="wrap_content"
 3    android:layout_height="wrap_content"/>
 4 
 5 <android.support.text.emoji.widget.EmojiAppCompatEditText
 6    android:layout_width="wrap_content"
 7    android:layout_height="wrap_content"/>
 8 
 9 <android.support.text.emoji.widget.EmojiAppCompatButton
10    android:layout_width="wrap_content"
11    android:layout_height="wrap_content"/>

 

绑定字体配置

 

EmojiCompat支持包绑定字体版本也是可用的. 这个包包含了嵌套元数据的字体. 也包含了使用AssetManager加载元数据和字体的BundledEmojiCompatConfig.
备注: 字体大小有好几MB.

 

添加支持包依赖

 

要想使用EmojiCompat支持包的绑定字体配置, 你必须修改开发环境中应用工程的类路径依赖.
 1 dependencies { 2 ... 3 compile "com.android.support:support-emoji-bundled:$version" 4 } 

 

使用绑定字体配置EmojiCompat

 

要使用绑定字体配置EmojiCompat, 执行下列步骤:
1, 使用BundledEmojiCompatConfig创建EmojiCompat实例并提供Context.
2, 调用init()方法初始化EmojiCompat, 并传入BundledEmojiCompatConfig实例.

1 public class MyActivity extends Activity {
2     @Override
3     public void onCreate() {
4         super.onCreate();
5         EmojiCompat.Config config = new BundledEmojiCompatConfig(this);
6         EmojiCompat.init(config);
7         ...
8     }
9 }

 

没有控件的情况下使用EmojiCompat

 

EmojiCompat使用EmojiSpan渲染正确的图片. 由此, EmojiCompat必须使用EmojiSpans将给定CharSequence转化成Spanned. EmojiCompat类提供了方法通过EmojiSpans将CharSequence转化成Spanned对象. 通过这个方法, 你能够处理和缓存已处理实例, 而不是原生字符串, 由此提供了应用的性能.
 1 CharSequence processed = EmojiCompat.get().process("neutral face \uD83D\uDE10"); 

 

IME使用EmojiCompat

 

使用EmojiCompat支持包, 键盘通过渲染用户正在交互的应用支持的emoji. IME能够使用hasEmojiGlyph()方法检测EmojiCompat是否有能力渲染emoji. 这个方法将一个emoji CharSequence作为形参, 如果EmojiCompat能够检测和渲染这个emoji的话, 会返回true.

键盘也能够检测应用支持的EmojiCompat支持包的版本, 以决定在画板中渲染哪个emoji. 要想检测这个版本, 如果可以的话, 键盘需要检测下列keys是否存在于EditorInfo.extras:

  • EDITOR_INFO_METAVERSION_KEY: 如果这个键存在, 这个值表示了应用使用的emoji元数据的版本. 如果这个键不存在, 应用不会使用EmojiCompat.
  • EDITOR_INFO_REPLACE_ALL_KEY: 如果该键存在且设置为true, 这表示应用调用了setReplaceAll()方法.

在EditorInfo.extras中接收到键之后, 键盘能够使用hasEmojiGlyph()方法, 在这个方法里面, metadataVersion是键EDITOR_INFO_METAVERSION_KEY的值, 来检测应用是否能够渲染特定的emoji.

 

在自定义控件中使用EmojiCompat

 

在应用中, 你总是能够使用process()方法来预处理CharSequence并把它添加到任何能够渲染Spanned实例的控件中. 比如, TextView. 此外, EmojiCompat提供如下控件帮助类让你花费最小的代价就能使用emoji支持丰富自定义控件.

  • EmojiTextViewHelper
  • EmojiEditTextHelper

Sample TextView:

 1 public class MyTextView extends AppCompatTextView {
 2    ...
 3    public MyTextView(Context context) {
 4        super(context);
 5        init();
 6    }
 7    ...
 8    private void init() {
 9        getEmojiTextViewHelper().updateTransformationMethod();
10    }
11 
12    @Override
13    public void setFilters(InputFilter[] filters) {
14        super.setFilters(getEmojiTextViewHelper().getFilters(filters));
15    }
16 
17    @Override
18    public void setAllCaps(boolean allCaps) {
19        super.setAllCaps(allCaps);
20        getEmojiTextViewHelper().setAllCaps(allCaps);
21    }
22 
23    private EmojiTextViewHelper getEmojiTextViewHelper() {
24        ...
25    }
26 }


Sample EditText:

 1 public class MyEditText extends AppCompatEditText {
 2    ...
 3    public MyEditText(Context context) {
 4        super(context);
 5        init();
 6    }
 7    ...
 8    private void init() {
 9        super.setKeyListener(getEmojiEditTextHelper().getKeyListener(getKeyListener()));
10    }
11 
12    @Override
13    public void setKeyListener(android.text.method.KeyListener keyListener) {
14        super.setKeyListener(getEmojiEditTextHelper().getKeyListener(keyListener));
15    }
16 
17    @Override
18    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
19        InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
20        return getEmojiEditTextHelper().onCreateInputConnection(inputConnection, outAttrs);
21    }
22 
23    private EmojiEditTextHelper getEmojiEditTextHelper() {
24        ...
25    }
26 }

QA:

 

  • 我该如何初始化字体下载?

如果Emoji字体在设备上并不存在, 那么在第一次请求的时候就会下载好. 下载调度对于应用是透明的.

  • 初始化花费多长时候?

在字体下载好之后, 初始化EmojiCompat大约花费150ms.

  • EmojiCompat支持包占用多大内存?

当前, 找到在应用内存中加载好的emoji并使用它的数据结构大约是200KB.

  • 自定义TextView可以使用EmojiCompat吗?

是的, EmojiCompat为自定义控件提供帮助类. 它也能够预处理给定字符串并将转换成Spanned.

  • 如果我在运行Android 4.4(API 19) - 的设备上, 布局文件中添加了控件, 会发生什么?

你能够在支持Android 4.4(API 19) - 的设备上引入EmojiCompat支持包或者它的控件. 然后, 如果设备运行的Android版本小于API 19, EmojiCompat和它的控件处理"no operation"状态. 这意味着EmojiTextView的行为就是一个常规的TextView. EmojiCompat实例, 在调用init()方法的时候, 马上就会进入LOAD_STATE_SUCCEED状态.

posted on 2018-07-09 16:56  SilentKnight  阅读(2146)  评论(0编辑  收藏  举报