黑暗模式
T9 输入法笔记(触屏版)
1. T9 输入法的中文字典数据
网上可以找到 T9 输入法的中文字典数据,但是通常有两个问题:
采用 GPL 协议,不太适合加入 AWTK。
只支持单个汉字的输入,不支持词组的输入。
经过考虑之后,决定自己生成 T9 输入法的中文字典数据。AWTK 使用了谷歌拼音输入法作为缺省的拼音输入法,谷歌拼音输入法的字典数据质量还是不错的,写一个工具把谷歌拼音输入法的字典数据转换为 T9 输入法的中文字典数据即可。
谷歌拼音输入法的字典数据原始格式为:
汉字 频率 简体/繁体标志 拼音
如:
中 243213.912993 0 zhong
转换工具要做的事情的是:
把拼音转换成对应的数字。比 zhong 对应的是 83553。
生成适合查询的格式。本来想复用谷歌拼音输入的引擎,但是没有成功,最后决定自己写程序来查询。
转换工具完整的代码请参考:awtk/tools/t9_gen/to_t9_zh.js
如果需要对字典进行裁剪,可以先编辑原始数据,然后重新生成数据。
2. T9 输入法的英文字典数据
要生成英文字典数据,获取一个英文单词列表,并且按照单词的使用频率进行排序。找 github 上找到了这样的数据。
同样需要写个小工具,把该数据转成生成适合查询的格式。
转换工具完整的代码请参考:awtk/tools/t9_gen/to_t9_en.js
该工具应该同样适用于其它西文语言。
3. 对键盘、输入法框架和编辑器进行改进
为了支持 T9 输入法,需要对键盘、输入法框架和编辑器进行改进。
随便提一下,SOLID 原则中的开闭原则通常只存在于理想(简单)情况下,一种切实可行的方法是,在早期设计时要考虑将来的扩展,但也无需过度设计,而是每次扩展时都对框架进行完善,让它更适应于将来的扩展。
3.1 支持语言切换
同样是在中文环境下,你可能需要输入中文,也可能需要输入英文。在中文键盘下输入中文,在英文键盘下输入英文。 在键盘切换时需要让输入法引擎知道当前的语言。
3.1.1 键盘
如果需要切换语言,可以给键盘的页面设置一个 lang 属性,用于标识改键盘的语言,切换到该页面时,自动设置输入法引擎的语言。
如:
xml
<view name="chinese" lang="zh_cn"
x="0" y="0" w="100%" h="100%" children_layout="default(r=4,c=4,s=2,m=2)">
<button repeat="300" name="," text=","/>
<button repeat="300" name="key:1" text="ABC"/>
<button repeat="300" name="key:2" text="DEF"/>
<button repeat="300" name="key:backspace" style="highlight">
<image draw_type="icon" image="backspace" x="center" y="middle" w="100%" h="100%"/>
</button>
<button repeat="300" name="key:3" text="GHI"/>
<button repeat="300" name="key:4" text="JKL"/>
<button repeat="300" name="key:5" text="MNO"/>
<button name="page:lower" style="highlight" text="abc"/>
<button repeat="300" name="key:6" text="PQRS"/>
<button repeat="300" name="key:7" text="TUV"/>
<button repeat="300" name="key:8" text="WXYZ"/>
<button name="page:symbols" style="highlight" text="#+="/>
<button repeat="300" name="opt:。?!" text="。?!"/>
<button repeat="300" name="space" text="Space"/>
<button name="action" style="highlight" text="Return"/>
<button name="page:number" style="highlight" text="123"/>
</view>
在切换键盘时,如果存在 lang 属性,将其设置到输入法。
c
value_t v;
widget_t* active = widget_get_child(iter, widget_get_value(iter));
if (widget_get_prop(active, WIDGET_PROP_IM_LANG, &v) == RET_OK) {
input_method_set_lang(input_method(), value_str(&v));
}
3.1.2 输入法提供设置语言的函数
c
/**
* @method input_method_set_lang
* 设置语言。
*
* > 有的输入法,同时支持输入多种语言。
* > 比如 T9,可以同时支持中文和英文输入,配合软键盘随时切换输入的语言。
*
* @annotation ["scriptable"]
* @param {input_method_t*} im 输入法对象。
* @param {const char*} lang 语言。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t input_method_set_lang(input_method_t* im, const char* lang);
3.1.3 输入法引擎提供设置语言的函数
c
/**
* @engine input_engine_set_lang
* 设置语言。
* @annotation ["scriptable"]
* @param {input_engine_t*} engine 输入法引擎对象。
* @param {const char*} lang 语言。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t input_engine_set_lang(input_engine_t* engine, const char* lang);
3.2 支持预编辑状态 (preedit)
预编辑状态是在文本真正提交到编辑器之前的状态,在预编辑状态下输入的文本,可以被后面的输入覆盖。比如软键盘上,一个按键有 ABC 三个字符,按一下输入 A,再按一下输入 B,继续按钮输入 C,在指定时间内没有按,则提交当前的字符到编辑器。
3.2.1 软键盘
键盘的按钮名称以"opt:"开头的按钮,表示这个按钮需要预编辑处理。为了支持预编辑输入,软键盘在按钮事件处理函数中,要做下列处理:
当前不在预编辑状态时,则进入预编辑状态,并启动定时器,把当前字符的序数设置为 0,并提交字符到编辑器。
当前在预编辑状态,更新定时器,把字符的序数设置为下一个,并提交字符到编辑器。
定时器超时或点击其它按钮,退出预编辑。
详情参考函数:keyboard_on_button_click
3.2.2 输入法
输入法需要提供几个函数用于中转消息:
c
/**
* @method input_method_dispatch_preedit
* 分发进入预编辑状态的事件。
* @annotation ["scriptable"]
* @param {input_method_t*} im 输入法对象。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t input_method_dispatch_preedit(input_method_t* im);
/**
* @method input_method_dispatch_preedit_confirm
* 分发确认预编辑状态的事件(提交预编辑内容,退出预编辑状态)。
* @annotation ["scriptable"]
* @param {input_method_t*} im 输入法对象。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t input_method_dispatch_preedit_confirm(input_method_t* im);
/**
* @method input_method_dispatch_preedit_abort
* 分发取消预编辑状态的事件(提交预编辑内容,退出预编辑状态)。
* @annotation ["scriptable"]
* @param {input_method_t*} im 输入法对象。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t input_method_dispatch_preedit_abort(input_method_t* im);
3.2.3 编辑器
编辑器需要处理相关事件:
c
case EVT_IM_PREEDIT: {
text_edit_preedit(edit->model);
break;
}
case EVT_IM_PREEDIT_CONFIRM: {
text_edit_preedit_confirm(edit->model);
break;
}
case EVT_IM_PREEDIT_ABORT: {
text_edit_preedit_abort(edit->model);
break;
}
具体请参考:edit.c/mledit.c
4. T9 输入法引擎的实现
T9 输入法引擎的实现很简单,不到 300 行代码。查找算法为:先精确查找,如果找到,将其添加到候选字列表中,否则使用前缀查找,在满足前缀的候选字中,挑选词频最高的,放到候选字列表中。
详情参考:input_engine_t9.c/t9.c