如何创建一个android输入法




为了创建一个输入法来输入信息到文本输入框或者其它View,你需要扩展 android.inputmethodservices.InputMtehodService类。这个API提供了很多一个输入法需 要的基本实现,比如管理输入法的状态以及可见性,还有就是如何与当前可见的activity通 信。

一个比较好的起点是SDK中SoftKeyboard示例代码。你可以修改这个代码来开始你自己的输 入法。

一个输入法和其它的程序或者服务的包是一样的。在AndroidManifest.xml文件中,你把输入法 声明为一个服务,然后给它设置特定的intent过滤器以及关联的元数据

<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>

如果你的输入法允许用户来设置一些东西,你就需要提供一个能够让Settings程序打开的activity。 这个是可选的,你也可以把所有的用户设置直接的放在你的输入法UI里面提供。

一个典型的InputMethodSErvice的生命周期是这样的:

可见元素

对于一个输入法一般有两个主要的可见元素,输入的view以及候选view。如果其中一个和 你的输入法体验无关的话,你也可以不用受这个规则的限制。

输入view

输入view是用户通过按键/手写/或者其它手势输入文本的view。当输入法第一次展示出来的 时候,InputMethdoService.onCreateInputView()会被调用。然后生成并且返回一个view在 输入法窗口中展示给用户。

候选View

这里是展示单词纠正或者单词补全并且让用户选择的地方。如果这与你的输入法可能无关, 你可以在调用InputMethodService.onCreateCandidatesView()的时候返回null,系统默认 是返回null的。

设计不同的输入类型

一个应用程序的文本输入可以有不同的输入类型,比如自由类型的文本,数字,URL,Email 地址以及搜索等等。当你实现一个新的输入法的时候,你需要注意不同的输入方式。系统不 会自动为不同的类型切换到不同的输入法,所以你需要在你在你的输入法里面支持各种不同 类型。当然, 输入法不负责检验输入到程序的内容,这是应用程序的责任。

比如,Android系统提供的拉丁输入法就提供了不同的方式来输入文本以及电话号码类型数 据:

InputMethodService.onStartInputView()调用的时候会传入一个EditorInof元素,它包含 了关于文本输入框类型以及其它属性的一些细节内容。

EditorInfo.inputType & EditorInfo.TYPE_CLASS_MASK可以是不同的值,包括: + TYPE_CLASS_NUMBER + TYPE_CLASS_DATETIME + TYPE_CLASS_PHONE + TYPE_CLASS_TEXT

可以查看android.text.InputType来查看更多的细节

EditorINfo.inputType可以包含其它的一些标志位来表示其它的一些标志。比如 TYPE_TEXT_VARIATION_PASSWORD 或者 TYPE_TEXT_VARIATION_URI 或者 TYPE_TEXT_FLAG_AUTO_COMPLETE

密码输入框

当你往密码输入框输入的时候你需要特别注意,确定用户输入的密码在你的UI上面看 不到,不管是输入View还是候选View。不要保存密码在其它地方,除非你有知会用户。

水平和垂直

UI需要能够自适应横向和竖直两种方向。在没有全屏输入法的模式下,留足够的空间让程序来 展示文本域以及相关的上下文。比较推荐的方式是,输入法不超过屏幕一半的高度。当然在 全屏输入法模式下,就不需要考虑这个问题。

发送文本到应用程序

有两种方式发送文本到一个应用程序。你可以发送一个一个的事件或者你可以在应用程序的 文本域里面编辑光标附近的文本。

发送一个key事件,你可以简单的构造一个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);

注意:在一些特定的类型的输入框(比如电话号码输入框)推荐使用上面的方法,因为过滤 器可能会在每个按键按下的时候执行过滤,如果用下面那种方法就会有问题。对于某些输入 类型返回按键和删除按键也需要像原始的按键事件来发送,因为程序可能会监测特殊的KEY Event来执行一个特殊的动作。

当我们编辑文本框中的文本的时候,android.view.inputmethod.InputConnection中的如下一些 方法可能会比较有用

  • getTextBeforeCurso()
  • getTextAfterCursor()
  • deleteSurroundingText()
  • commitText()

比如,我们假设”Fell”文本是在光标的左边,然后你需要替换它为”Hello!”

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

在提交之前组织好文本

如果你的输入法做一些文本预测或者需要多个步骤来组织一个词或者图像字符的时候,你可 以在输入框中展示进度直到用户提交词语,然后你可以把部分的提交替换为完整的输入。正 在输入的文本可以在文本框中突出显示,比如加上一个下划线

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

拦截硬件的按键事件

虽然输入法窗口没有明显的聚焦,它是第一个接受硬件按键事件的,它可以选择消费这些事 件或者把这些事件传递给应用程序。比如你可能想消费一个直接的按键来在你的候选View里 面让用户来选择,或者你可能会捕获返回按键来取消输入法窗口生成的一些弹窗。为了监听 硬件key,覆盖InputMethodService.onKeyDown()以及InputMethodService.onKeyUp()方法 。当你自己不需要消费一个特殊的按键的时候,记得调用super.onKey*方法。

其它的一些注意事项

  • 在输入法UI给用户提供一个可以简单的打开相关设置的页面的途径。
  • 在输入法UI给用户提供一个可以直接去切换到其它输入法的途径。
  • 快速地弹出UI,预加载或者延迟加载一些比较大的一个资源,这样用户可以在触摸输入框
    的时候很快地看到输入法界面。尽量缓存任何资源以及界面状态来等待下一次对于这个
    输入法的调用
  • 另外一方面,任何的大内存分配都需要在输入法隐藏的时候释放,这样应用程序才有足
    够的内存来执行。可以考虑使用一个延迟的信号来释放资源,比如在输入法在隐藏状态
    时间达到几秒的时候。
  • 保证大部分普通的字符串能够在输入法中输入,因为用户可能会在密码或者用户名中使用
    一些标点符号,我们不应该让用户在它需要输入某个特殊字符来打开一个被密码锁住的设
    备的时候卡住。

例子

作为一个真实世界中的例子,我们支持了很多输入类型以及文本的预测。可以参考 LatingIME source code。Android1.5 SDK也提供了一个软键盘的例子。