黑暗模式
7. 输入法
本章导读:
AWTK提供了多种输入键盘,可以根据应用的具体情况灵活搭配选择一种或者多种键盘,还可以根据自己的喜好设置不同的窗体样式。
7.1 简介
输入法是GUI重要的组件之一,虽然实现起来并不是太复杂,但其涉及的组件比较多,理解起来还是比较困难的,这里介绍一下AWTK中输入法的内部架构,详见下图:
- input_method sdl实现包装了原生输入法(SDL的实现有些问题,还需要进一步完善)
- input_method_null实现只是提供了空的实现,不启用输入法和软键盘
- input_method default提供了AWTK自己的实现,负责软键盘的打开关闭和输入法引擎的创建
7.2 软键盘
在嵌入式系统中,通常没有物理键盘,所以需要在屏幕上实现软键盘。AWTK中的软键盘是一个普通的窗口,其中的按钮和候选字控件,都是用AWTK的UI描述文件定义的,可以方便实现各种不同的软键盘。软键盘的描述文件放在awtk/demos/assets/default/raw/ui目录下,文件名以kb_打头。与普通窗口相比,软键盘有以下不同:
- 被点击时不会影响原有编辑器的焦点
- 不接受按键事件和输入文本事件
在AWTK给的演示示例awtk-examples目录下的demo默认都启动了软键盘功能。例如,在UI界面的XML文件中添加edit编辑器控件,并设置好input_type输入类型,就可以弹出软键盘了,具体示例请看7.5章节。如果不想启动键盘请添加宏WITH_NULL_IM。
7.3 键盘类型
AWTK为了开发方便,提供了多种类型的键盘,详见下表。其中"输入类型"对应edit编辑控件的input_type属性,代码详见7.5章节。
输入类型 | 键盘描述文件 | 说明 |
---|---|---|
phone | kb_phone | 电话号码 |
int | kb_int | 整数 |
float | kb_float | 浮点数 |
uint | kb_uint | 非负整数 |
ufloat | kb_ufloat | 非负浮点数 |
hex | kb_hex | 16进制 |
kb_ascii | 邮件地址 | |
password | kb_ascii | 密码 |
custom | 自定义的键盘的XML文件 | 使用自定义的键盘 |
custom_password | 自定义的密码软键盘的XML文件 | 使用自定义的密码软键盘 |
ipv4 | kb_uint | IP V4 地址(如:192.168.1.1) |
date | kb_uint | 日期(如:2020/02/20) |
time | kb_uint | 时间(时分,如:12:00) |
time_full | kb_uint | 时间(时分秒,如:12:00:00) |
不输入 | kb_default | 默认 |
7.4 键盘映射
AWTK提供了一套自己的键盘映射关系,详见下表,因为内容过多该表没有列出全部,完整的映射关系请看:在AWTK_API手册根据关键字key_code_t查看。
例如,在键盘上按下F2键对应在AWTK的是TK_KEY_F2,相关示例请看3.5.4章节的第一小节。
名称 | 说明 |
---|---|
TK_KEY_RETURN | 回车键 |
TK_KEY_BACKSPACE | 删除键 |
TK_KEY_TAB | Tab键 |
TK_KEY_SPACE | 空格键 |
TK_KEY_0 | 数字键0 |
TK_KEY_1 | 数字键1 |
TK_KEY_2 | 数字键2 |
TK_KEY_a | 字母键a |
TK_KEY_b | 字母键b |
TK_KEY_F1 | F1键 |
TK_KEY_F2 | F2键 |
... | ... |
7.5 示例
因为程序不一定需要键盘,如果程序需要使用键盘需要做下面两件事:
- 拷贝UI文件:根据需要将awtk/demos/assets/default/raw/ui目录下文件名以kb_打头UI文件拷贝到应用程序assets/default/raw/ui目录下。
- 拷贝图片:因为有些键盘还需要使用到图片(使用到的图片可以打开kb_开头的UI文件看看),所以还需要将awtk/demos/assets/default/raw/images下对应的图片拷贝到应用程序assets/default/raw/images目录下。
下面以awtk/bin/demoui.exe下的KeyBoard页面为例。当点击edit编辑框控件的时候,AWTK会根据edit设置的input_type键盘类型,弹出不同的键盘,以kb_开头的键盘UI文件还可以根据实际的屏幕大小适当的修改它的高度以及样式等。如果edit输入类型不填,将默认使用kb_default键盘,效果如下:
UI文件具体代码如下:
xml
<!-- awtk/demos/assets/default/raw/ui/keyboard.xml -->
<window anim_hint="htranslate">
<list_view x="0" y="0" w="100%" h="-50" item_height="36" auto_hide_scroll_bar="true">
<scroll_view name="view" x="0" y="0" w="-12" h="100%">
<list_item style="empty" children_layout="default(r=1,c=0)">
<label w="30%" text="Name"/>
<edit w="70%" text="" tips="name"/>
</list_item>
<list_item style="empty" children_layout="default(r=1,c=0)">
<label w="30%" text="Desc"/>
<edit w="70%" text="" tips="desc"/>
</list_item>
<list_item style="empty" children_layout="default(r=1,c=0)">
<label w="30%" text="Int"/>
<edit w="70%" text="" tips="int" input_type="int"/>
</list_item>
<list_item style="empty" children_layout="default(r=1,c=0)">
<label w="30%" text="UInt"/>
<edit w="70%" text="" tips="unsigned int" input_type="uint"/>
</list_item>
<list_item style="empty" children_layout="default(r=1,c=0)">
<label w="30%" text="Float"/>
<edit w="70%" text="" tips="float" input_type="float"/>
</list_item>
...
</window>
7.6 自定义键盘
有时AWTK自带的键盘,可能不能满足应用程序的需要。例如,需要将软键盘嵌入到窗口内部(比如计算器和密码输入等),这时可以使用自定义软键盘,这里以AWTK中的demoui为例,详见下图:
实现自定义键盘的步骤如下:
(1) 设置edit的input_type为"custom"(它会禁止启用内置的软键盘),代码如下:
xml
<!-- awtk/demos/assets/default/raw/ui/soft_keyboard.xml -->
<window text="Custom Soft Keyboard" anim_hint="htranslate" >
<edit x="c" y="10" w="90%" h="30" focus="true" input_type="custom" text="" tips="custom"/>
...
</window>
如果希望初始化时编辑器自动获的焦点,可以设置focus为true。
(2) 软键盘的按钮放入一个view(任何容器控件均可)中,并将view的is_keyboard设置为true,代码如下:
xml
<!-- awtk/demos/assets/default/raw/ui/soft_keyboard.xml -->
<window text="Custom Soft Keyboard" anim_hint="htranslate" >
...
<view y="60" x="c" w="90%" h="-60" is_keyboard="true"
children_layout="default(r=4,c=4,m=5,s=5)" >
<button name="key" text="0" />
<button name="key" text="1" />
<button name="key" text="2" />
<button name="key" text="3" />
<button name="key" text="4" />
<button name="key" text="5" />
<button name="key" text="6" />
<button name="key" text="7" />
<button name="key" text="8" />
<button name="key" text="9" />
<button name="key" text="#" />
<button name="backspace" text="<=" />
</view>
</window>
(3) 处理按钮事件
处理正常按键,代码如下:
c
/* awtk/demos/demo_ui_app.c */
static ret_t on_send_key(void* ctx, event_t* e) {
widget_t* button = WIDGET(e->target);
char text[2];
text[0] = (char)button->text.str[0];
text[1] = '\0';
input_method_commit_text(input_method(), text);
return RET_OK;
}
处理删除键,代码如下:
c
/* awtk/demos/demo_ui_app.c */
static ret_t on_backspace(void* ctx, event_t* e) {
input_method_dispatch_key(input_method(), TK_KEY_BACKSPACE);
return RET_OK;
}
如果你不希望出现编辑器的光标,可以使用label控件代替edit控件,输入和删除时直接操作label的text。
7.7 软键盘的action按钮
在Android、iPhone等手机的软键盘上有一个特殊的按钮,这个按钮的功能和显示的文本与当前的编辑操作密切相关,在不同的编辑器(edit控件)中,可能显示"发送"、"下一个"、"回车"等。
AWTK的软键盘也有这样一个特殊的按钮,称为软键盘的action按钮。在AWTK内置的软键盘中,该action按钮的默认文本为"Return",例如kb_default软键盘。
下面以demoui为例,实现Text编辑器唤醒kb_default软键盘时,action按钮显示文本为"Next",如下图所示,实现步骤请看下文。
7.7.1 添加action按钮
在软键盘的UI文件(kb_default.xml)中定义action按钮,即将button的name属性设置为action,代码如下:
xml
<!-- awtk/design/default/ui/kb_default.xml -->
<keyboard theme="keyboard" x="0" y="bottom" w="100%" h="40%">
<pages x="0" y="bottom" w="100%" h="-28" active="4">
<view name="upper" x="0" y="0" w="100%" h="100%" children_layout="default(r=4,c=1,s=2,m=2)">
...
<group_box children_layout="default(r=1,c=0,s=2,m=2)">
...
<button name="action" style="highlight" w="20%" text="Return"/>
</group_box>
</view>
...
</pages>
...
</keyboard>
7.7.2 修改action按钮显示文本
将Text编辑器(edit控件)的action_text属性设置为Next,当该编辑器唤醒软键盘时会根据action_text属性设置软键盘中action按钮的文本,代码如下:
xml
<!-- awtk/design/default/ui/edit.xml -->
<window anim_hint="htranslate" >
<list_view x="0" y="0" w="100%" h="-50" item_height="36" auto_hide_scroll_bar="true">
<scroll_view name="view" x="0" y="0" w="-12" h="100%">
...
<list_item style="empty" children_layout="default(r=1,c=0,ym=1)">
<label w="30%" text="Text"/>
<edit name="edit" w="70%" left_margin="34" tips="searth" min="0" max="150"
step="0.1" action_text="Next">
<image draw_type="icon" image="find" x="0" y="0" w="30" h="100%" />
</edit>
</list_item>
...
</scroll_view>
<scroll_bar_d name="bar" x="right" y="0" w="12" h="100%" value="0"/>
</list_view>
<button name="close" x="center" y="bottom:10" w="25%" h="30" text="Close"/>
</window>
需要注意的是:以上代码中设置了kb_default软键盘action按钮的text为Return,因此该软键盘的action按钮默认显示为"Return"。
如果没有设置编辑框的action_text属性,那么被唤醒的软键盘的action按钮的显示其text属性的值,此处为"Return";如果设置了编辑框的action_text属性,那么被唤醒的软键盘的action按钮优先显示编辑框action_text属性的值。
7.7.3 实现action按钮功能
实现action按钮的自定义功能,可以注册对应编辑器EVT_IM_ACTION事件,例如,在指定编辑器唤醒的软键盘中点击action按钮,打印该编辑器设置的action_text,代码如下:
c
/* EVT_IM_ACTION事件的回调函数 */
static ret_t on_action_event(void* ctx, event_t* evt) {
widget_t* target = WIDGET(evt->target);
edit_t* edit = EDIT(target);
log_debug("edit action_text: %s \n", edit->action_text);
return RET_OK;
}
/* 注册编辑器EVT_IM_ACTION事件 */
ret_t application_init(void) {
widget_t* win = window_open("home_page");
widget_t* edit = widget_lookup(win, "edit", TRUE);
widget_on(edit, EVT_IM_ACTION, on_action_event, NULL);
return RET_OK;
}
完整示例请参考 edit.c。
7.8 加入中文输入法
在有些示例项目中,没有加入输入法,主要是开发板的flash不够。如果flash够大(不小于4M时),可以按下面步骤加入中文输入法,步骤如下:
(1)添加AWTK目录下的相关源代码
- 加入3rd/gpinyin/src中的代码
- 加入src/input_engines/input_engine_pinyin.cpp
- 去掉src/input_engines/input_engine_null.cpp
- include路径加入3rd/gpinyin/include
- 把3rd/gpinyin/data目录下的gpinyin.dat文件(拼音输入法字典)拷贝到项目的资源目录中,例如:res/assets/default/raw/data。
- 把default_full.ttf拷贝到default.tff,重新生成资源并编译(defualt.ttf没有汉字,default_full.ttf里才有,否则输入的字符无法显示)。
(2)拼音输入法需要:设置INPUT_ENGINE='pinyin'以及定义宏WITH_IME_PINYIN,具体可修改awtk/awtk_config.py文件,代码如下:
python
# awtk/awtk_config.py
...
#INPUT_ENGINE='null'
#INPUT_ENGINE='spinyin'
#INPUT_ENGINE='t9'
#INPUT_ENGINE='t9ext'
INPUT_ENGINE='pinyin'
...
if INPUT_ENGINE == 't9':
COMMON_CCFLAGS = COMMON_CCFLAGS + ' -DWITH_IME_T9 '
elif INPUT_ENGINE == 't9ext' :
COMMON_CCFLAGS = COMMON_CCFLAGS + ' -DWITH_IME_T9EXT'
elif INPUT_ENGINE == 'pinyin' :
COMMON_CCFLAGS = COMMON_CCFLAGS + ' -DWITH_IME_PINYIN '
elif INPUT_ENGINE == 'spinyin' :
COMMON_CCFLAGS = COMMON_CCFLAGS + ' -DWITH_IME_SPINYIN '
elif INPUT_ENGINE == 'null' :
COMMON_CCFLAGS = COMMON_CCFLAGS + ' -DWITH_IME_NULL '
...
关于输入法的其他说明,请看:awtk/src/input_engines/README.md文档。
(3)在UI界面XML中添加edit编辑器控件,不要设置input_type属性,点击下图中右侧edit编辑器控件可以弹出默认中文输入法键盘。
具体代码如下:
xml
<!-- awtk/demos/assets/default/raw/ui/edit.xml -->
<window anim_hint="htranslate" >
<list_view x="0" y="0" w="100%" h="-50" item_height="36" auto_hide_scroll_bar="true">
<scroll_view name="view" x="0" y="0" w="-12" h="100%">
...
<list_item style="empty" children_layout="default(r=1,c=0,ym=1)">
<label w="30%" text="Text"/>
<edit w="70%" left_margin="34" tips="searth" min="0" max="150" step="0.1">
<image draw_type="icon" image="find" x="0" y="0" w="30" h="100%" />
</edit>
</list_item>
...
</scroll_view>
<scroll_bar_d name="bar" x="right" y="0" w="12" h="100%" value="0"/>
</list_view>
<button name="close" x="center" y="bottom:10" w="25%" h="30" text="Close"/>
</window>
7.9 更新拼音输入法字典和联想字库
拼音输入法字典保存了输入的拼音和出现的汉字之间的对应关系,例如输入拼音"wo",会出现汉字"我",如下图左侧所示。
联想字库是指在输入了某个汉字或词组后,输入法根据该汉字或词组提供其常用的组词,例如输入汉字"我"后,根据常用组词"我们"、"我国"、"我省"等,输入法会提供"们"、"国"、"省"等汉字,如下图右侧所示。
在某些情况下,开发者需要自己更新拼音输入法字典和联想字库。比如:使用更好的输入法字典、去掉一些不需要的汉字或者使用更完善的联想字库,具体方法详见下文。
7.9.1 更新拼音输入法字典
更新拼音输入法字典的步骤如下:
1. 修改配置文件
(1)配置文件awtk/3rd/gpinyin/data/valid_utf16.txt:
该文件中存放了有效的utf16编码的汉字。汉字要正常显示,则必须要存在于该文件中,当需要去掉一些不必要的汉字时,可以删除该文件中对应的汉字,但该文件通常不做修改。
(2)配置文件awtk/3rd/gpinyin/data/rawdict_utf16_65105_freq.txt:
该文件中存放了拼音对应的汉字以及该汉字的使用频率,使用频率越高,输入拼音时对应汉字的排名就越前,比如配置文件中"戏剧"的使用频率比"喜剧"高,那么在输入拼音"xiju"时,"戏剧"就排在"喜剧"之前,如下图所示。
如果想要输入拼音"xiju"时,"喜剧"排在"戏剧"之前,只需让"喜剧"的使用频率高于"戏剧"即可,例如此处将"喜剧"的使用频率改为1300.00000000即可。
2. 重新生成字典数据
修改完配置文件后,需要重新生成字典数据,在awtk目录下打开终端,运行awtk/bin目录下的gpinyingen程序,执行以下命令:
bash
./bin/gpinyingen
该程序通过上面两个配置文件生成awtk/3rd/gpinyin/data/gpinyin.dat,该文件就是更新后的拼音输入法字典。
3. 在项目中使用gpinyin.dat
将生成的awtk/3rd/gpinyin/data/gpinyin.dat文件拷贝到项目资源目录的data目录下,例如:res/assets/default/raw/data。
若项目是用Designer创建的,那么可以将gpinyin.dat文件拷贝到项目的design/default/data目录中,再使用Designer打包项目资源。
7.9.2 更新联想字库
更新拼音输入法的联想字库有两种方式,一种是抓取网页数据生成words.json文件,另一种是直接使用现有数据生成words.json文件,然后通过words.json生成words.bin文件,最终通过words.bin文件更新联想字库。
由于更新联想字库需要执行js脚本文件,所以开发者需要先安装nodejs,安装过程可自行上网搜索。更新联想字库的步骤如下:
(1)生成words.json文件
方式一:通过抓取网页数据生成words.json,在awtk/tools/word_gen目录中打开终端,执行脚本gen_words_json.js,抓取网页,生成words.json文件,命令如下:
bash
node gen_words_json.js
可修改脚本 gen_words_json.js 中的 maxURLS 改变最大网页数量。
方式二:通过现有数据生成words.json,在awtk/tools/word_gen目录中打开终端,执行脚本to_json.js,使用现有的数据,即chinese_with_freq.txt,生成words.json文件,命令如下:
bash
node to_json.js
chinese_with_freq.txt是从 GitHub 下载的,开发者可自行下载需要的联想字库配置文件,若想手动修改该文件,修改方式可参考7.9.1章节更新拼音输入法字典中修改配置文件。
(2)通过words.json生成二进制的words.bin文件,在awtk/tools/word_gen目录中打开终端,执行脚本to_words_bin.js,命令如下:
bash
node to_words_bin.js
(3)通过生成的words_bin更新demoui的联想字库,将words_bin拷贝到demoui的资源目录,例如demos/assets/default/raw/data,然后重命名为suggest_words_zh_cn.dat。
此处以Windows平台为例,在awtk根目录下打开终端,执行以下命令:
bash
copy tools/word_gen/words.bin demos/assets/default/raw/data/suggest_words_zh_cn.dat
(4)如果开发者在awtk/awtk_config.py文件中没有定义宏WITH_FS_RES,即不支持文件系统,那么还需要重新打包资源,在awtk根目录打开终端,执行以下命令:
bash
python scripts/update_res.py all
AWTK默认定义宏WITH_FS_RES,即支持文件系统。
需要注意的是,基于nodejs的中文分词模块(segment)有可能出现OOM异常(内存溢出),这是nodejs安装目录下node_modules/segment/lib/module/DictTokenizer.js脚本文件中getChunks函数导致的。
如果以上遇到问题,可以通过限制chunks.length的大小解决,例如限制chunks.length为5000,代码如下:
js
var getChunks = function (wordpos, pos, text) {
...
for (var i = 0; i < words.length; i++) {
var word = words[i];
var nextcur = word.c + word.w.length;
if (!wordpos[nextcur]) {
ret.push([word]);
} else {
var chunks = getChunks(wordpos, nextcur);
for (var j = 0; j < chunks.length && j < 5000; j++) {
ret.push([word].concat(chunks[j]));
}
}
}
...
return ret;
};