如何开发一个OPhone平台的输入法应用
OPhone平台内的输入法开发主要包括:
- 上层界面UI的开发
- 底层输入法引擎的开发
本文所介绍的开发技术包括:
- 在OPhone平台下构建输入法应用
- 设计键盘,进行界面开发
值得说明的是,输入法的很多核心功能都是体现在底层输入法引擎中的。输入法引擎的功能包括:
- 根据输入字符获取候选词以及联想词
- 调整管理词库词频等等。
简而言之,输入法引擎是一个语言邻域专用的数据库引擎,根据用户按键输入,在语言数据库中查询出候选,供用户选择。这些与OPhone平台的开发是相互独立的。
下面我们以简单的实例,说明如何开发一个OPhone平台的输入法应用。
1、配置服务
输入法应用在OPhone系统中是一个service。与其他service一样,输入法需要通过在AndroidManifest.xml中进行service定义。示例如下:
<application android:label="@string/english_ime_name"> <service android:name="LatinIME" android:label="@string/english_ime_name" 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> </application>
- 该service具有BIND_INPUT_METHOD权限,表明这是一个输入法服务;
- 在intent-filter中使用android.view.InputMethod action来定义;
- 最后通过name为android.view.im的meta-data来描述该输入法的一些属性;
meta-data引用的是一个XML文件,该文件是输入法的配置文件,用来配置一些信息,例如:
- 是否为默认输入法;
- 是否具有配置Activity来配置输入法的一些选项;
如果指定了配置 Activity,则在系统设置界面中的输入法设置中可以启动该Activity来设置输入法的配置项。
2、继承InputMethodService
通过扩展android.inputmethodservice.InputMethodService可以很容易的实现一个输入法服务。
InputMethodService提供了一些系统回调函数,可以按照需要来实现。
构成一个输入法应用,最重要的界面元素包括:
- 软键盘区域
- 候选词区域
InputMethodService为这两个区域设置了专门的回调函数,以便开发者灵活的定制并加载资源文件。下面针对这两个区域的回调函数,做一个简单的介绍。
2.1 onCreateInputView
- 该函数在输入区域,(比如虚拟键盘)第一次显示的时候被调用,仅调用一次。
- 显示的View通过return返回,默认的返回是null,当返回为null时,什么也不显示。
- 当输入区域显示后,可以通过实现onEvaluateInputViewShown()来控制。
- 如果想替换显示的输入区域可以通过setInputView(View)实现。
- 生成输入区域的View通过LayoutInflater加载Layout中的资源文件来实现。
- 在资源文件中,或者是一个KeyboardView,或者是由若干个KeyboardView组成的Container。
通常在这个函数中都需要注册键盘事件的监听器setOnKeyboardActionListener。这个监听器用来处理以下键盘事件:
- onKey(int primaryCode, int[] keyCodes)
- onPress(int primaryCode)
- onRelease(int primaryCode)
- onText(CharSequence text)
SoftKeyboard.onCreateInputView()方法,通过layoutInflater加载layout中的布局文件来创建KeyboardView对象。
@Override public View onCreateInputView() { mInputView = (KeyboardView) getLayoutInflater().inflate(R.layout.input, null); mInputView.setOnKeyboardActionListener(this); mInputView.setKeyboard(mQwertyKeyboard); return mInputView; }
layout/input.xml
<com.example.android.softkeyboard.LatinKeyboardView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/keyboard" android:layout_alignParentBottom="true" android:layout_width="fill_parent" android:layout_height="wrap_content" />
2.2 onCreateCandidatesView
- 该函数在联想词条第一次被显示的时候调用,仅调用一次。
- 显示的View通过return返回。如果没有联想条,则返回null。
- 控制是否显示联想词条,可以通过setCandidatesViewShown(boolean)来实现。
- 如果想替换联想词条则通过实现setCandidatesView(View)来实现。
- 构造联想词条,通过LayoutInflater来加载资源文件,生成View。
SoftKeyboard.onCreateCandidatesView()方法,创建类CandidateView的对象。
@Override public View onCreateCandidatesView() { mCandidateView = new CandidateView(this); mCandidateView.setService(this); return mCandidateView; }
3、软键盘区域
- 在OPhone平台中软键盘是很容易实现的;
- 通过Keyboard 类可以创建软键盘,该类从XML文件中读取软键盘信息。
- 软键盘信息包括有多少行,每行有多少按键,每个按键代表什么内容等等。
在SoftKeyboard.onInitializeInterface()方法,创建类LatinKeyboard的3个对象。类LatinKeyboard继承类Keyboard。
@Override public void onInitializeInterface() { Log.i(tag, "++onInitializeInterface++"); if (mQwertyKeyboard != null) { // Configuration changes can happen after the keyboard gets recreated, // so we need to be able to re-build the keyboards if the available // space has changed. int displayWidth = getMaxWidth(); if (displayWidth == mLastDisplayWidth) return; mLastDisplayWidth = displayWidth; } mQwertyKeyboard = new LatinKeyboard(this, R.xml.qwerty); mSymbolsKeyboard = new LatinKeyboard(this, R.xml.symbols); mSymbolsShiftedKeyboard = new LatinKeyboard(this, R.xml.symbols_shift); }
在xml目录中有3个文件qwerty.xml、symbols.xml、symbols_shift.xml用来定义了3种不同的虚拟键盘。
3.1 Keyboard
Keyboard通过加载XML文件,构建虚拟键盘。XML文件的内容示例如下:
<Keyboard android:keyWidth="10%p" android:horizontalGap="0px" android:verticalGap="0px" android:keyHeight="@dimen/key_height"> <Row android:verticalGap="@dimen/key_padding"> <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left" android:popupKeyboard="@xml/kbd_popup_template" android:popupCharacters="@string/alternates_for_q" /> </Row> </Keyboard>
- horizontalGap、verticalGap、keyWidth、keyHeight分别用来表示横向、纵向的间距以及按键宽、高。
- Row用来表示行,每行包括若干Key。
- 最左侧和最右侧的Key需要用android:keyEdgeFlags标示。
- android:popupKeyboard和android:popupCharacters用来标示按键后弹出的小键盘的资源id和显示的文字。
填写完成构建键盘的XML文件后,需要编写一个类来进行具体的操作处理。这个类继承自Keyboard类。在这个类中,需要实现的方法有:
- 构造函数:通过构造函数加载键盘资源文件;
- createKeyFromXml:如果需要在按键类增加某些特定属性,可以通过继承Keyboard.Key类来实现,这时应相应的在此函数中增加这些特色属性的初始化工作。
LatinKeyboard类的实例方法createKeyFromXml(Resources, Row, int, int, XmlResourceParser)
@Override protected Key createKeyFromXml(Resources res, Row parent, int x, int y, XmlResourceParser parser) { Key key = new LatinKey(res, parent, x, y, parser); if (key.codes[0] == 10) { mEnterKey = key; } return key; }
内部静态类LatinKeyboard.LatinKey
static class LatinKey extends Keyboard.Key { public LatinKey(Resources res, Keyboard.Row parent, int x, int y, XmlResourceParser parser) { super(res, parent, x, y, parser); } /** * Overriding this method so that we can reduce the target area for the key that closes the keyboard. */ @Override public boolean isInside(int x, int y) { return super.isInside(x, codes[0] == KEYCODE_CANCEL ? y - 10 : y); } }
3.2 KeyboardView
KeyboardView用来渲染一个虚拟键盘,并监控键盘上面的点击和触摸事件。输入法应用需要构建一个继承自KeyboardView的键盘视图类。在这个类中处理所有来自键盘的事件。重要的函数包括:
- setKeyboard:用来绑定Keyboard和KeyboardView;
- onLongPress:用来处理长按事件;
- getOnKeyboardActionListener:在KeyboardView中有一个监听器——OnKeyboardActionListener。通过这个函数可以获得此键盘视图的监听器。监听器通过OnKey函数分发处理键盘点击事件;
- getKeyboard:通过此函数获取和view绑定的键盘类。
LatinKeyboardView类的实例方法onLongPress(Key)
@Override protected boolean onLongPress(Key key) { if (key.codes[0] == Keyboard.KEYCODE_CANCEL) { getOnKeyboardActionListener().onKey(KEYCODE_OPTIONS, null); return true; } else { return super.onLongPress(key); } }
4、候选词区域
对于中文输入法来说,候选区域是一个特别重要的内容,拿拼音输入法来说用户输入拼音会出现多个候选词语,通过对候选词语的合理安排是一个输入法是否好用的重要评判标准之一。但是也有特殊情况下不需要候选区域的,例如输入数字或则密码。候选区域通过继承View实现。
4.1 CandidateView
- 候选栏不同于键盘,OPhone输入法框架中没有为他封装特定的类。输入法候选栏需要继承View,独立实现。
- 在这个类中,要处理所有点击在候选栏的操作。包括点击候选词的操作。
- 输入法应用在初始化时,会将自己的引用或者专门用来处理点击候选词操作的listener传递给CandidateView中。
- 具体的选中候选词的操作要通过调用输入法应用服务内部的函数完成。
- CandidateView中一个重要的函数是onDraw(),通过此函数绘画候选栏。
- CandidateView中需要设置用来保存候选词页数的变量,通过页数,计算候选词显示的词语个数,已调整宽度。
- 通过Canvas的drawText方法写出各候选词条。
- CandidateView中还可以定义一个PopupWindow用来显示点中后弹出的文字内容。
- 弹出窗口可以在CandidateView中构建,也可以由CandidateContainer传递进来。
4.2 CandidateContainer
- 之所有构建一个容器,是为了保存多个CandidateView。
- 候选词条上的左右方向的翻页按钮,也是通过这个容器来管理的。
- 这个类不是OPhone平台框架实现的类,借鉴了Android平台自带的LatinIME,我认为这是一个不错的实现方 式。
- 细化了CandidateView的功能,处理了除候选词外的其他逻辑操作。
另外使用CandidateContainer还有一个优点,即,通过保存两个CandidateView,翻页处理时可以马上显示,而不需要重新加载,提高了显示效率。CandidateContainer是通过ViewFlipper来管理多个View的。