(翻译) Creating an Input Method

原文来自Android SDK文档中的 docs/resources/articles/creating-input-method.html

编写输入法(IME)需要扩展 InputMethodService类。 这个类提供了输入法的基本实现,主要是管理输入法的状态和可见性以及与当前可见Activity的通信。

SDK中的SoftKeyboard是学习输入法的一个好例子。 可以修改这个示例代码来建立自己的输入法。 

输入法打包成应用或服务, 跟其他应用类似。 在AndroidManifest.xml中, 声明输入法为一个Service, 包括适当的intent filter和其他的一些元信息。 

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.fastinput">

    <application android:label="@string/app_label">

        <!-- Declares the input method service -->
        <service android:name="FastInputIME"
            android:label="@string/fast_input_label"
            android:permission="android.permission.BIND_INPUT_METHOD">
            <intent-filter>
                <action android:name="android.view.InputMethod" />
            </intent-filter>
            <meta-data android:name="android.view.im" android:resource="@xml/method" />
        </service>

        <!-- Optional activities. A good idea to have some user settings. -->
        <activity android:name="FastInputIMESettings" android:label="@string/fast_input_settings">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
            </intent-filter>
        </activity> 
    </application>
</manifest>
 

如果输入法允许用户进行某些设置, 需要提供一个用于设置的Activity。 这个Activity可以从Settings应用启动。 这里的Activity是可选的, 也可以直接在IME的UI里面直接提供用户设置。

扫描二维码关注公众号,回复: 727942 查看本文章

InputMethodService典型的生命周期如下:

可视元素

输入法中有两个主要的可视元素, 输入视图和备选视图(原文:the input view and the candidates view)。 如果某个视图无关输入法的用户体验, 则不必按照这个风格。 

输入视图

输入视图是用户可以输入文本的地方, 输入形式可以是按键、手写或手势。 当输入法第一次显示出来, InputMethodService.onCreateInputView()方法会被调用。 在该方法中创建并返回将要在输入法窗口中显示的View树结构。 

备选视图

备选视图用于单词纠正,或者显示输入完成情况, 可供用户选择。 再次强调 , 这个备选视图可能与输入法相关也可能不相关, 如果不相关可以从InputMethodService.onCreateCandidatesView()返回null。 缺省情况该方法返回null。 

设计不同的输入类型

可以指定应用的文本框类型, 比如可自由输入、数字、URL、邮箱或搜索。 实现一个新的输入法时, 需要意识到有不同的输入类型。 输入法不会自动在输入类型之间切换, 所以自定义的输入法需要支持所有的输入类型。 另外, IME不校验发送给应用的输入内容。 输入检验由应用自己完成。 

比如, Android的LatinIME为文本输入和电话号码输入分别提供不同的布局:




 
 InputMethodService.onStartInputView()方法接受一个EditorInfo对象作为参数, 该对象包含输入类型的具体细节以及the application's text field(???)的其他一些属性。 

(EditorInfo.inputType & EditorInfo.TYPE_CLASS_MASK) 可以是下列某个值, 

  • TYPE_CLASS_NUMBER
  • TYPE_CLASS_DATETIME
  • TYPE_CLASS_PHONE
  • TYPE_CLASS_TEXT

详细内容可参考android.text.InputType

EditorInfo.inputType还包含用于指示class variation(???)的掩码位和其他标志位。 比如, TYPE_TEXT_VARIATION_PASSWORD 或 TYPE_TEXT_VARIATIION_URI 或 TYPE_TEXT_FLAG_AUTO_COMPLETE.

密码域

当往密码框输入内容时需要注意, 应当保证密码在UI上不可见——不管是在输入视图还是备选视图。 并且, 不要在不明确提示用户的情况下保存密码。 

风景模式和肖像模式

UI应该可以适应横屏和竖屏。 在非全屏输入法模式下, 留足够的空间让应用显示文本输入框以及相关的上下文内容。 更可取的方法是, IME不应用占用超过屏幕一半的空间。 全屏输入法不用考虑这个问题。 

发送文本到应用

有两种方式从输入法发送文本到应用, 可以直接发送单个按键事件,或者编辑文本框中光标附近的文本。 

构造KeyEvent对象并调用InputConnection.sendKeyEvent()即可发送按键事件到应用。 下面是一个例子:

InputConnection ic = getCurrentInputConnection();
long eventTime = SystemClock.uptimeMillis();
ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
    KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0,
    KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
    KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0,
    KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));

 也可以使用以下方法

InputMethodService.sendDownUpKeyEvents(keyEventCode);

 注意:建议某些输入框比如电话号码输入框使用第一种方法, 因为每次按键事件之后可能有过滤器应用于输入的文本。 对某些输入类型而言, 回车键和删除键也应当使用原始按键事件来发送(sent as raw key events), 因为应用可能正在监听特定的按键事件, 以便执行某些操作。 

当编辑文本框中的文本时, 可使用android.view.inputmethod.InputConnection中的方法:

  • getTextBeforeCursor()
  • getTextAfterCursor()
  • deleteSurroundingText()
  • commitText()

比如, "Fell"位于输入光标的左边, 你想将它替换为"Hello!":

InputConnection ic = getCurrentInputConnection();
ic.deleteSurroundingText(4, 0);
ic.commitText("Hello", 1);
ic.commitText("!", 1);
 

提交前组合文本(Composing Text)

如果输入法进行文本预测或者需要多个步骤来组成一个单词或文字, 可以在文本输入框中显示这个过程, 直到用户提交整个单词。 这时可以使用完整的文本替换之前不完整的文本。 正在进行组合的文本可使用某种形式的高亮显示, 比如下划线。 

InputConnection ic = getCurrentInputConnection();
ic.setComposingText("Composi", 1);
...
ic.setComposingText("Composin", 1);
...
ic.commitText("Composing ", 1);
 


 


拦截实体按键事件

尽管输入法窗口没有明确的获得焦点, 它仍然可以首先接收到实体按键事件,然后可以选择是否处理这些事件还是传递事件到应用。 比如, 组合文本过程时, 处理方向键事件,以在UI中导航到候选的单词(原文:you may want to consume the directional keys to navigate with your UI to candidate selection during composition)。 或者, 点击了Back键, 隐藏所有从输入法窗口中弹出来的菜单。 拦截实体按键事件需要重写InputMethodService.onKeyDown()和InputMethodService.onKeyUp()。 如果不想自己处理某个按键, 记住要调用 super.onKeyXXX()!

其他注意事项

  • 提供便于用户直接从输入法界面打开相关设置界面的途径
  • 提供便于用户从输入法界面上在不同输入法(设备上可能安装有多个输入法)之间切换的途径
  • 快速显示界面——预加载或延迟加载大资源, 以便用户点击文本输入框时可以很快看到输入法界面。 缓存资源和视图, 加速下次调用输入法的过程
  • 另一方面, 隐藏输入法窗口后尽早释放分配的大的内存块,以便应用有足够的内存。 当输入法进入隐藏状态时, 可以使用几秒的延迟消息来释放资源
  • 确保输入法可输入最常用的字符, 比如用户可能在密码或用户名中输入标点, 要避免出现用户无法输入某个字符的情况而无法使用有密码保护的设备

例子

参考LatinIME的源码, 这是一个实际使用的输入法, 并且可支持文本预测及多种输入类型。 SDK中也包含一个SoftKeyboard例子。 

猜你喜欢

转载自410063005.iteye.com/blog/1768313